集合中对象到对象属性的映射

发布于 2024-09-07 14:12:09 字数 3197 浏览 6 评论 0原文

我创建一个(动态生成的类型)集合以在 silverlight 网格中显示,其中一个过程涉及创建一个导入(动态生成的类型)类型,然后将导入类型上的属性映射到(动态生成的类型)两种类型的集合共享一个 Id 属性来标识该项目(无论是在网格上还是在导入中),

即 类型绑定到网格

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

并导入 ids 匹配的类型

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

我想复制 foos.

将集合中的属性从一种类型映射到另一种类型的快速方法是什么?

编辑

这是最终的类型映射器实现,感谢Stephan,因为该功能仅在键成员相等时映射两种类型,通过代表成员名称的字典字符串定义的映射,适用于银光。

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

}

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱你不解释 2024-09-14 14:12:09
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) 并将其设置为我的代码片段的导入值。

编辑

动态方法

本文提供了一个生成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});

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

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 DynamicMethod to 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:

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

You should be able to do GetMapper(sourceType,destinationType).Invoke(null,new [] { myObject});

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文