64

I have an object model MyObject with various properties. At one point, I have two instances of these MyObject: instance A and instance B. I'd like to copy and replace the properties in instance A with those of instance B if instance B has non-null values.

If I only had 1 class with 3 properties, no problem, I could easily hard code it (which is what I started doing). But I actually have 12 different object models with about 10 properties each.

What's good way to do this?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
frenchie
  • 51,731
  • 109
  • 304
  • 510
  • 4
    have you ever tried sth like AutoMapper (http://automapper.codeplex.com/)? –  Jan 02 '12 at 15:37
  • 3
    The obvious choice is reflection but you will pay a performance penalty... – jondavidjohn Jan 02 '12 at 15:37
  • 2
    Take a look at this answer; I think it covers your case: http://stackoverflow.com/questions/571982/iterating-over-class-properties – StilesCrisis Jan 02 '12 at 15:38
  • @jondavidjohn: what kind of performance penalty are we talking about? – frenchie Jan 02 '12 at 15:43
  • @frenchie performance: pretty significant, but comparatively speaking. Here's an IL approach for the same: http://stackoverflow.com/questions/7422861/looking-for-a-fast-and-easy-way-to-coalesce-all-properties-on-a-poco - looks like an exact duplicate, in fact – Marc Gravell Jan 02 '12 at 16:53

7 Answers7

79

Update Use AutoMapper instead if you need to invoke this method a lot. Automapper builds dynamic methods using Reflection.Emit and will be much faster than reflection.'

You could copy the values of the properties using reflection:

public void CopyValues<T>(T target, T source)
{
    Type t = typeof(T);

    var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);

    foreach (var prop in properties)
    {
        var value = prop.GetValue(source, null);
        if (value != null)
             prop.SetValue(target, value, null);
    }
}

I've made it generic to ensure type safety. If you want to include private properties you should use an override of Type.GetProperties(), specifying binding flags.

Brad
  • 15,361
  • 6
  • 36
  • 57
Bas
  • 26,772
  • 8
  • 53
  • 86
15

I have tried what's described by Merge Two Objects into an Anonymous Type by Kyle Finley and it is working perfectly.

With the TypeMerger, the merging is as simple as

var obj1 = new {foo = "foo"};

var obj2 = new {bar = "bar"};

var mergedObject = TypeMerger.MergeTypes(obj1 , obj2 );

That's it! You've got the merged object. Apart from that, there is a provision to ignore specific properties too. You can use the same thing for MVC3 as well.

ruffin
  • 16,507
  • 9
  • 88
  • 138
Naveen Vijay
  • 15,928
  • 7
  • 71
  • 92
3

You can do it using reflection, but as someone stated, it'll have a performance penalty.

Since you're working with an expected class design, you can achieve the same goal by using an extension method like so:

public static class MyClassExtensions
{
    public static void Merge(this MyClass instanceA, MyClass instanceB)
    {
        if(instanceA != null && instanceB != null)
        {
             if(instanceB.Prop1 != null) 
             {
                 instanceA.Prop1 = instanceB.Prop1;
             }


             if(instanceB.PropN != null) 
             {
                 instanceA.PropN = instanceB.PropN;
             }
    }
}

And later, somewhere in your code:

someInstanceOfMyClass.Merge(someOtherInstanceOfMyClass);

At the end of the day you've centralized this operation in an extension method and if you add or remove a property of your class, you only need to modify extension method's implementation and you'll get everything done.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • 3
    This will still cause maintainability issues as the OP mentioned – Bas Jan 02 '12 at 15:49
  • 1
    @BasB I'm not sure about this, because as I wrote in my answer, it's a predictable design and I doubt this class will add or remove tons of properties overtime. So, at the end of the day, as I said, it has the same effect as doing it with reflection, without its performance penalty. – Matías Fidemraizer Jan 02 '12 at 15:52
  • 1
    @BasB In addition, these maintainability issues can be solved using a T4 template in Visual Studio so removing or adding a property can trigger a code generation process and re-create this extension method. Why not? WCF and a lot of .NET frameworks are solving this using code generation :) – Matías Fidemraizer Jan 02 '12 at 15:54
2

I've created a method similar to some answers above, but it returns a new object merged between 2 objects, and will only merge the second objects value if the first objects value is null.

public T MergeObjects<T>(T primary, T secondary)
        {
            T obj = (T)Activator.CreateInstance(typeof(T));
            Type t = typeof(T);

            var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);

            foreach (var prop in properties)
            {
                var value = prop.GetValue(primary, null);
                if (value != null)
                    prop.SetValue(obj, value, null);
                else
                {
                    value = prop.GetValue(secondary, null);
                    if (value != null)
                        prop.SetValue(obj, value, null);
                }
            }
            return obj;
        }
big boy
  • 315
  • 2
  • 13
1

you can use this package:XASoft

use foo.Merger(bar) method to combine 2 object to a dynamic object

just like this

var objA = new { a = 1, b = 2, c = 3 };
var newObj = objA.Merger(new { a = "Hey", d = 4, e = 5 });
newObj.e = "There";
newObj.f = 6;
Console.WriteLine(JsonConvert.SerializeObject(newObj));

even if object list works fine too!

private class TestClass
{
    public int X { get; set; }
    public int Y { get; set; }
}
static void Main(string[] args)
{
    var list = new List<TestClass> {
        new TestClass{ X=1,Y=2},
        new TestClass{ X=3,Y=4}
    };
    Console.WriteLine(JsonConvert.SerializeObject(list.ListMerger(i => new
        { X = "null value", Z = i.X == 1 ? 0 : 1 })));
}

oops,looks like javascript language...

btw,source code click here

AlexXie
  • 11
  • 2
0

This is the same as @Bas Answer but for Merging 2 Object lists

public class Copycontents
{
    public static void Work<T>(IList<T> targetList, IList<T> sourceList, Func<T, int> selector)
    {
        var matchingPrimaryKey = targetList.Select(x => selector(x)).ToList();

        foreach (var thismatchingPrimaryKey in matchingPrimaryKey)
        {
            CopyValues<T>(targetList.Single(x => selector(x) == thismatchingPrimaryKey),
                sourceList.Single(x => selector(x) == thismatchingPrimaryKey));
        }
    }

    private static void CopyValues<T>(T target, T source)
    {
        Type t = typeof(T);

        var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);

        foreach (var prop in properties)
        {
            var value = prop.GetValue(source, null);
            if (value != null)
                prop.SetValue(target, value, null);
        }
    }
}
Rob
  • 26,989
  • 16
  • 82
  • 98
Yehia Amer
  • 608
  • 6
  • 11
0

I've written my own class for this purpose: ObjectMerger.

Basically it uses reflections (and may be slow because of that). It also contains more features e.g. parsing objects for cyclic references and merge them too. My ObjectMerger also contains mechanism to handle more complex classes like Delegate or MemberInfo. Those will be copied completely, other objects in the class are recursively merged.

The Syntax is like:

var initialInstance = new MyObjectBase(); // Initialize first object
var properInstance = new MyObjectWithAlgorithms(); // Initialize second object
var result = ObjectMerger.MergeObjects(properInstance, initialInstance); // Merge Objects into type of "properInstance"

I'm sorry to say that it is NOT FOR USE AS IS, because some external libraries are missing in the repository at the moment due to limitations in my company, but they can easily be rewritten. I hope a can add them in future.

Markus Weber
  • 1,059
  • 1
  • 11
  • 24