Отображение свойств объекта в объект в коллекциях - PullRequest
0 голосов
/ 24 июня 2010

Я создаю коллекцию (динамически генерируемый тип) для отображения в сетке Silverlight, и один из процессов включает создание типа импорта (динамически генерируемый тип) с последующим сопоставлением свойств типа импорта с коллекцией (динамически генерируемый тип). ) оба типа имеют общее свойство Id, которое идентифицирует элемент (будь то в сетке или при импорте)

т.е. тип привязан к сетке

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

и импортируйте тип

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

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

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

EDIT

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

1 голос
/ 24 июня 2010
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) и установить для него значение import для моего фрагмента.

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

Динамические методы

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

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

Фактический пример:

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});

...