2010-06-24 5 views
0

Im создание коллекции (динамически сгенерированного типа) для отображения в сетке silverlight и один из процессов включает создание типа импорта (динамически сгенерированного типа), а затем сопоставление свойств по типу импорта сбор (динамически типа) оба типа обладают свойством Id, который идентифицирует элемент (будь то на сетке или в импорте)Сопоставление объектов с объектами в коллекциях

т.е. типа, связанного с сеткой

int Id {get; set}  
string Foo {get;set;} 
string FooFoo {get;set;} 

и импортировать тип

int Id {get; set} 
string Foo {get;set} 

где идентификаторы совпадают, я хочу скопировать foos.

Что такое быстрый способ сопоставления свойств одного типа другому в коллекции?

EDIT

Heres окончательная реализация Typemapper с благодарностью Stephan, как функция будет отображать только два типа, когда keymembers равны, отображение, определенное с помощью словаря строки строки, представляющего имена элементов, произведения 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); 
    } 

} 

ответ

1
bound.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList() 
    .ForEach(s => 
     { 
      var prop = import.GetType().GetProperty(s.Name,BindingFlags.Public | BindingFlags.Instance); 
      if(prop != null) 
      { 
       prop.SetValue(import,s.GetValue(bound,null),null); 
      } 
     }); 

Это будет отображать свойства от одного элемента к другому. Если вы хотите сделать это в коллекции, сделайте этот метод и сделайте myCollection.Select(o => MapProperties(o,mapType));.

Примечание: В настоящее время метод использует существующий объект и копирует его. Вы можете использовать свой метод, кроме типа, а затем вызвать Activator.CreateInstance(type) и установить его для значения импорта для моего фрагмента.

Редактировать

Dynamic Methods

Эта статья имеет хороший пример генерации DynamicMethod сделать глубокую копию динамических объектов. У него будет намного больше времени настройки, чем решение отражения, но каждый последующий вызов будет таким же быстрым, как если бы он был скомпилирован.

Редактировать

Actual Пример:

DynamicMethod GetMapper(Type type1, Type type2) 
{ 
DynamicMethod method = new DynamicMethod("junk", type2, 
new Type[] { type1 }); 

ILGenerator il = method.GetILGenerator(); 

LocalBuilder obj0 = il.DeclareLocal(type2); //target 

// create object and store in local 0 
ConstructorInfo ctor = type2.GetConstructor(
    new Type[] { }); 
il.Emit(OpCodes.Newobj, ctor); 
il.Emit(OpCodes.Stloc_0); 


PropertyInfo[] properties = type1.GetProperties(BindingFlags.Instance 
| BindingFlags.Public | BindingFlags.FlattenHierarchy); 
foreach (PropertyInfo prop in properties) 
{ 
// local constructed object 
il.Emit(OpCodes.Ldloc_0); 

// load source argument 
il.Emit(OpCodes.Ldarg_0); 

// get property value 
il.EmitCall(OpCodes.Callvirt, type1.GetMethod(
    "get_" + prop.Name), null); 
il.EmitCall(OpCodes.Callvirt, type2.GetMethod(
    "set_" + prop.Name), null); 
} 

il.Emit(OpCodes.Ldloc_0); 
il.Emit(OpCodes.Ret); 
return method; 
} 

Вы должны быть в состоянии сделать GetMapper(sourceType,destinationType).Invoke(null,new [] { myObject});

+0

я сделал нечто подобное, оказывается медленным (с помощью отражения), когда мы учитывая несколько тысяч. все еще +1 –

+0

Если типы динамически генерируются, это, скорее всего, лучшее, что вы собираетесь делать. Вы можете немного ускорить его, используя 'Reflection.Emit', чтобы сгенерировать делегат фабрики, а затем запустить его, но я не уверен, какой именно путь это сделать. Вы можете взглянуть на MSDN для 'Reflection.Emit', поскольку это может дать хорошее направление. – Stephan

+0

Забыл, что это вопрос Silverlight. Мое второе решение, вероятно, не будет работать для Silverlight. Это можно было бы сделать как операцию на стороне сервера (например, с RIA), но, как чисто клиентская сторона, скорее всего, это не сработает. – Stephan