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

后端 未结 5 1695
伪装坚强ぢ
伪装坚强ぢ 2020-12-30 11:10

I have two classes with exactly same members (properties and fields) with same datatypes. I want to map the members from one to other in automated way. I know there are more

相关标签:
5条回答
  • 2020-12-30 11:47

    Mapping libraries such as ValueInjector or AutoMapper are a great help for exactly this sort of functionality.

    Using AutoMapper you would create a mapping using something like this

    Mapper.CreateMap<MyObject1,MyObject2>();
    

    It has a number of default conventions, one of which is that by default it will copy properties with identical types/names.

    And then actually do a mapping like this

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

    Of course you can do this easy enough with reflection as well, but with libraries such as these someone has put a lot of thought into adding all sorts of handy mapping functionality, as well as done performance tuning. AutoMapper, for example, uses IL generation to read values instead of reflection so it is significantly faster for repeatedly mapping things (very hand for mapping big collections of things)

    0 讨论(0)
  • 2020-12-30 11:47

    Extending from accepted answer from @pasty, I have created generic method for this purpose.

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

    One may need to alter the filters on generic types; but everything else will work cross any types. Null check is added for fieldOfDestination and propertyOfDestination just in case a member is missing; this adds up little more flexibility.

    0 讨论(0)
  • 2020-12-30 11:50

    There are a number of tools that do this. Look at the "TranslateTo" routine in service stack as an example. They have excellent auto mapping (https://github.com/ServiceStack/ServiceStack/wiki/Auto-mapping).

    Using this, all you would have to do is:

     obj2 = obj1.TranslateTo<MyObject2>();
    

    Simple and elegant!

    In case you are interested a few other references to similar topics:

    • https://stackoverflow.com/questions/286294/object-to-object-mapper
    • Best Practices for mapping one object to another
    • http://www.softwarerockstar.com/2011/05/complex-object-mapping-using-automapper/
    0 讨论(0)
  • 2020-12-30 11:57

    One possibility to make this (for example for the purpose of creating your own automapper or understand how it basically works) would be to use (as already suggested) Reflection. The code can look like this:

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

    The function can be used like this:

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

    The output is:

    a - 1
    

    Please note, that the usage of reflection comes with a price - it costs performance. Using reflection you can access both private and public object members. This is for example used from Visual Studio to create test accessor objects in order to access all test object members.

    Please have a look at the existing automappers (see the other answers for links) and use them instead of reinventing the wheel by yourself - the existing libraries are optimized for speed, thoroughly tested and are very comfortable to use. This way you will minimize errors in your code.

    0 讨论(0)
  • 2020-12-30 12:05

    AutoMapper is another good tool for something like this http://automapper.codeplex.com/

    You map from one object to another with something like this:

    Mapper.CreateMap<MyClass, MyDTO>();
    var myDTO = Mapper.Map<MyClass, MyDTO>(myClass);
    
    0 讨论(0)
提交回复
热议问题