繁体   English   中英

如果两个不同的类具有相同的属性,如何自动映射两个不同类的实例之间的值?

[英]How to automatically map the values between instances of two different classes if both have same properties?

我有两个具有相同数据类型的完全相同成员(属性和字段)的类。 我想以自动方式将成员从一个映射到另一个。 我知道有更实用的开发方法可以处理。 一种简单的解决方案是手动将每个成员从一个实例映射到另一个实例。 但是,我想将此自动化作为一些通用解决方案。

假设您有以下代码:

public MyObject1 AssignTest (MyObject1 obj1, MyObject2 obj2)
{
    //Code here for auto map
}

其中MyObject1MyObject2具有相同数据类型的完全相同的属性。 我不想通过并单独分配值(即MyObject1.Property1 = MyObject2.Property1等)。 是否可以将MyObject1指定的所有值自动分配给MyObject2

实现这一点的一种可能性(例如为了创建自己的自动映射器或了解它的基本工作原理)是使用(如已经建议的) Reflection 代码可能如下所示:

// TODO: error handling
// Test classes
public class A
{
    public string Name { get; set; }
    public int Count;
}

public class B
{
    public string Name { get; set; }
    public int Count;
}
// copy routine
public B CopyAToB(A a)
{
    B b = new B();
    // copy fields
    var typeOfA = a.GetType();
    var typeOfB = b.GetType();
    foreach (var fieldOfA in typeOfA.GetFields())
    {
        var fieldOfB = typeOfB.GetField(fieldOfA.Name);
        fieldOfB.SetValue(b, fieldOfA.GetValue(a));
    }
    // copy properties
    foreach (var propertyOfA in typeOfA.GetProperties())
    {
        var propertyOfB = typeOfB.GetProperty(propertyOfA.Name);
        propertyOfB.SetValue(b, propertyOfA.GetValue(a));
    }

    return b;
}

该函数可以这样使用:

var a = new A
{
    Name = "a",
    Count = 1
};

var b = CopyAToB(a);
Console.Out.WriteLine(string.Format("{0} - {1}", b.Name, b.Count));

输出是:

a - 1

请注意,反射的使用是有代价的——它会降低性能。 使用反射,您可以访问私有和公共对象成员。 例如,这在 Visual Studio 中用于创建测试访问器对象,以便访问所有测试对象成员。

请查看现有的自动映射器(请参阅链接的其他答案)并使用它们而不是自己重新发明轮子- 现有库针对速度进行了优化,经过全面测试并且使用起来非常舒适。 这样,您将最大限度地减少代码中的错误。

ValueInjectorAutoMapper 之类的映射库对这类功能有很大帮助。

使用 AutoMapper,您将使用这样的方法创建映射

Mapper.CreateMap<MyObject1,MyObject2>();

它有许多默认约定,其中之一是默认情况下它将复制具有相同类型/名称的属性。

然后实际做一个这样的映射

var myObject2 = Mapper.Map<MyObject1,MyObject2>(myObject1);

当然,您也可以通过反射轻松地做到这一点,但是对于诸如此类的库,有人花了很多心思添加各种方便的映射功能,并进行了性能调整。 例如,AutoMapper 使用 IL 生成来读取值而不是反射,因此重复映射事物的速度要快得多(非常适合映射大型事物集合)

有许多工具可以做到这一点。 以服务堆栈中的“TranslateTo”例程为例。 他们有出色的自动映射( https://github.com/ServiceStack/ServiceStack/wiki/Auto-mapping )。

使用这个,你所要做的就是:

 obj2 = obj1.TranslateTo<MyObject2>();

简单而优雅!

如果您对类似主题的其他一些参考感兴趣:

从@pasty接受的答案扩展而来,我为此创建了通用方法。

public static TDest MapSourceToDest<TSource, TDest>(TSource source)
                                    where TSource : class//We are not creating an instance of source, no need to restrict parameterless constructor
                                    where TDest : class, new()//We are creating an instance of destination, parameterless constructor is needed
{
    if(source == null)
        return null;

    TDest destination = new TDest();

    var typeOfSource = source.GetType();
    var typeOfDestination = destination.GetType();

    foreach(var fieldOfSource in typeOfSource.GetFields())
    {
        var fieldOfDestination = typeOfDestination.GetField(fieldOfSource.Name);
        if(fieldOfDestination != null)
        {
            try
            { fieldOfDestination.SetValue(destination, fieldOfSource.GetValue(source)); }
            catch(ArgumentException) { }//If datatype is mismatch, skip the mapping
        }
    }

    foreach(var propertyOfSource in typeOfSource.GetProperties())
    {
        var propertyOfDestination = typeOfDestination.GetProperty(propertyOfSource.Name);
        if(propertyOfDestination != null)
        {
            try
            { propertyOfDestination.SetValue(destination, propertyOfSource.GetValue(source)); }
            catch(ArgumentException) { }//If datatype is mismatch, skip the mapping
        }
    }

    return destination;
}

可能需要更改泛型类型的过滤器; 但其他一切都适用于任何类型。 fieldOfDestinationpropertyOfDestination添加空检查,以防万一缺少成员; 这增加了一点灵活性。

AutoMapper 是另一个很好的工具,例如http://automapper.codeplex.com/

您可以使用以下内容从一个对象映射到另一个对象:

Mapper.CreateMap<MyClass, MyDTO>();
var myDTO = Mapper.Map<MyClass, MyDTO>(myClass);

我改进并概括了@keenthinker 响应,它始终适用于任何类型的对象。

调用静态方法并传递地图类和对象

var result = SimpleMapper.Map<FromModel, ToModel>(data);

实现:

public static class SimpleMapper
{
    public static T Map<F, T>(F from)
    {
        // inicialize return object
        T b = (T)Activator.CreateInstance(typeof(T));

        // copy fields
        Type typeOfA = typeof(F);
        Type typeOfB = typeof(T);
        foreach (var fieldOfA in typeOfA.GetFields())
        {
            var fieldOfB = typeOfB.GetField(fieldOfA.Name);
            fieldOfB.SetValue(b, fieldOfA.GetValue(from));
        }
        // copy properties
        foreach (var propertyOfA in typeOfA.GetProperties())
        {
            var propertyOfB = typeOfB.GetProperty(propertyOfA.Name);
            propertyOfB.SetValue(b, propertyOfA.GetValue(from));
        }

        return (T)b;
    }
}

问题很老,但使用 JsonConvert 使它更快?

MyObject2 obj2 = JsonConvert.DeserializeObject<MyObject2>(JsonConvert.SerializeObject(obj1));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM