Im creating a collection of (dynamically generated type) for display in a silverlight grid and one of the processes involves creating an import (dynamically generated type) type then mapping the properties on the import type to the collection of (dynamically generated type) both types share a Id property that identifies the item ( be it on the grid or in the import)
ie
type bound to grid
int Id {get; set}
string Foo {get;set;}
string FooFoo {get;set;}
and import the type
int Id {get; set}
string Foo {get;set}
where ids match i want to copy foos.
What is a fast way to map properties from the one type to another in a collection?
EDIT
Heres the final Typemapper implementation with thanks to Stephan, as a feature will only map the two types when the keymembers are equal, mappings defined via a dictionary string string representing the member names, works in silverlight.
public class TypeMapper
{
private readonly DynamicMethod _mapper;
public static DynamicMethod BuildMapper(Type fromType,
Type toType,
KeyValuePair<string, string> keyMemberMap,
Dictionary<string, string> memberMappings)
{
var method = new DynamicMethod("Map", typeof(bool), new[] { fromType, toType });
// Preparing Reflection instances
MethodInfo getFromKeyMethod = fromType.GetMethod(
string.Format("get_{0}", keyMemberMap.Key),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo getToKeyMethod = toType.GetMethod(
string.Format("get_{0}", keyMemberMap.Value),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
ILGenerator gen = method.GetILGenerator();
// Preparing locals
gen.DeclareLocal(typeof(Boolean));
// Preparing labels
Label labelNoMatch = gen.DefineLabel();
// Writing body
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Callvirt, getFromKeyMethod);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Callvirt, getToKeyMethod);
gen.Emit(OpCodes.Ceq);
gen.Emit(OpCodes.Stloc_0);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Brfalse_S, labelNoMatch);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldarg_0);
foreach (var mapping in memberMappings)
{
var getFromValueMethod = fromType.GetMethod(
string.Format("get_{0}", mapping.Key),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var setToValueMethod = toType.GetMethod(
string.Format("set_{0}", mapping.Value),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
gen.Emit(OpCodes.Callvirt, getFromValueMethod);
gen.Emit(OpCodes.Callvirt, setToValueMethod);
}
gen.MarkLabel(labelNoMatch);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Ret);
return method;
}
public void Map (object fromInstance, object toInstance)
{
_mapper.Invoke(null, new[] { fromInstance, toInstance });
}
public TypeMapper(Type fromType, Type toType,
KeyValuePair<string, string> keyMemberMap,
Dictionary<string, string> memberMappings)
{
_mapper = BuildMapper(fromType, toType, keyMemberMap, memberMappings);
}
}
That will map properties from one item to another. If you want to do it in a collection make that a method and do
myCollection.Select(o => MapProperties(o,mapType));.Note: The method currently uses an existing object and copies into it. You could have your method except a type and then call
Activator.CreateInstance(type)and set that to the value of import for my snippet.Edit
Dynamic Methods
This article has a good example of generating a
DynamicMethodto do the deep copy of dynamic objects. It will have a much longer setup time then the reflection solution, but each subsequent call will be as fast as if compiled.Edit
Actual Example:
You should be able to do
GetMapper(sourceType,destinationType).Invoke(null,new [] { myObject});