43

How can I merge two anonymous types, so that the result contains the properties of both source objects?

var source1 = new
{
    foo = "foo",
    bar = "bar"
}

var source2 = new
{
    baz = "baz"
}

var merged = Merge(source1, source2) // <-- here's where the magic should happen

// merged: 
// {
//      foo = "foo",
//      bar = "bar",
//      baz = "baz"
// }
DLeh
  • 23,806
  • 16
  • 84
  • 128
davehauser
  • 5,844
  • 4
  • 30
  • 45

4 Answers4

47

So here's, what I finally came up with (inspired by @BlueMonkMN's answer):

public dynamic Merge(object item1, object item2)
{
    if (item1 == null || item2 == null)
        return item1 ?? item2 ?? new ExpandoObject();

    dynamic expando = new ExpandoObject();
    var result = expando as IDictionary<string, object>;
    foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties())
    {
        result[fi.Name] = fi.GetValue(item1, null);
    }
    foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties())
    {
        result[fi.Name] = fi.GetValue(item2, null);
    }
    return result;
}
davehauser
  • 5,844
  • 4
  • 30
  • 45
15
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;

namespace ConsoleApplication1
{
  class Program
  {
     static void Main(string[] args)
     {
        var source1 = new
        {
            foo = "foo",
            bar = "bar"
        };

        var source2 = new
        {
           baz = "baz"
        };

        dynamic merged = Merge(source1, source2);

        Console.WriteLine("{0} {1} {2}", merged.foo, merged.bar, merged.baz);
     }

     static MergedType<T1, T2> Merge<T1, T2>(T1 t1, T2 t2)
     {
        return new MergedType<T1, T2>(t1, t2);
     }
  }

  class MergedType<T1, T2> : DynamicObject
  {
     T1 t1;
     T2 t2;
     Dictionary<string, object> members = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);

     public MergedType(T1 t1, T2 t2)
     {
        this.t1 = t1;
        this.t2 = t2;
        foreach (System.Reflection.PropertyInfo fi in typeof(T1).GetProperties())
        {
           members[fi.Name] = fi.GetValue(t1, null);
        }
        foreach (System.Reflection.PropertyInfo fi in typeof(T2).GetProperties())
        {
           members[fi.Name] = fi.GetValue(t2, null);
        }
     }

     public override bool TryGetMember(GetMemberBinder binder, out object result)
     {
        string name = binder.Name.ToLower();
        return members.TryGetValue(name, out result);
     }
  }
}
ITmeze
  • 1,902
  • 2
  • 21
  • 32
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • Thanks again. Played around a bit with this. Any reason why you make `MergedType` generic? It works as well with `object` for the sources (`static MergedType Merge(object item1, object item2)` and then `... fi in item1.GetType().GetProperties()`. I came then back to ExpandoObject, which seems to provide a similar solution, but with less code. I added my final solution to my question. – davehauser Jul 20 '11 at 00:37
  • No, I was thinking about that after I posted and realized that my initial attempts at making a more strongly-typed solution were futile and using templates was probably entirely unnecessary. – BlueMonkMN Jul 20 '11 at 11:02
  • However, if you had any reason to still want to be able to access the members of T1 and T2 via MergedType, you could probably do it if you kept the templates. Intellisense would probably be able to show you that MergedType had a property of type T1 if you created one, and what it's members were. – BlueMonkMN Jul 20 '11 at 11:06
11

You can't, if what you're expecting is a single object where you can access the properties in a compile-time type-safe way (as opposed to a result which is purely dynamic at execution time). The closest you could come would be:

var merged = Tuple.Create(source1, source2);

Console.WriteLine(merged.Item1.foo);
Console.WriteLine(merged.Item1.bar);
Console.WriteLine(merged.Item2.baz);

Bear in mind that anonymous types are created at compile-time. It's not like they're "dynamic" types. You could use ExpandoObject in .NET 4 for that, but it's not quite the same as an anonymous type with all the relevant properties in.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    So, that's why I didn't find the answer in your book... :-) – davehauser Jul 19 '11 at 22:56
  • Is it possible to convert an anonymous type to an ExpandoObject? So I could merge them (cast as `IDictionary` should do). If the output is an ExpandoObject, that would also work in my case. – davehauser Jul 19 '11 at 22:58
  • Hey, actally you *can*! Look at the second answer I posted (much better than my first) :) – BlueMonkMN Jul 19 '11 at 23:14
  • Oh, I see now that ExpandoObject is very similar (and more appropriate) that DynamicObject, which is what I found. – BlueMonkMN Jul 19 '11 at 23:21
  • Ok, I chose BlueMonkMN's answer. Although Jon answered my concrete question correctly, BlueMonkMN led me to a viable solution. – davehauser Jul 20 '11 at 00:43
4

The following works in .NET 3.5 (and probably 2.0 as well). I modified davehauser's answer.

    public static object MergeJsonData(object item1, object item2)
    {
        if (item1 == null || item2 == null)
            return item1 ?? item2 ?? new object();

        var result = new Dictionary<string, object>();
        foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties().Where(x => x.CanRead))
        {
            var Value = fi.GetValue(item1, null);
            result[fi.Name] = Value;
        }
        foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties().Where(x => x.CanRead))
        {
            var Value = fi.GetValue(item2, null);
            result[fi.Name] = Value;
        }
        return result;
    }
m-albert
  • 1,089
  • 8
  • 15