2011-07-19 145 views
24

如何合并两个匿名类型,以便结果包含两个源对象的属性?合并匿名类型

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" 
// } 
+0

如果您使用的是C#4,请使用'dynamic'用动态成员创建对象。 – BoltClock

+0

@Dave,你能发表你想出来的方法吗?它会更明显。我喜欢它,我想加入它。 :) – BrunoLM

+0

@BrunoLM,刚刚发布我的解决方案。去投票:-) – davehauser

回答

30

所以这里的,我终于想出了(由@ BlueMonkMN的回答启发):

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

+1 Nice方法。简单并且效果很好。 – BrunoLM

+0

您可能需要在'GetProperties()'调用中添加'.Where(x => x.CanRead)'来避免只写属性的情况。 –

+1

这是一个匿名对象。所有的属性都应该可读。 –

-1
 var merged = new 
    { 
     source1 = source1, 
     source2 = source2 
    }; 

 var merged2 = new 
    { 
     foo = source1.foo, 
     bar = source1.bar, 
     baz = source2.baz 
    }; 
+0

其实,只有第二个片段。第一个问题完全错误。 – BoltClock

+1

我认为关键是要制作一个通用的“合并”方法,如果事先不知道类型,这种方法在这里不起作用。 –

+0

@Jon Skeet,是的,你是对的。 – davehauser

12

你不能。你能来最接近的将是:

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

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

记住,匿名类型在编译时创建。这不像他们是“动态”类型。你可以在为.NET 4中使用ExpandoObject,但它不是完全一样的匿名类型,在所有相关的属性。

+2

所以,这就是为什么我没有在您的书中找到答案... :-) – davehauser

+0

是否可以将匿名类型转换为ExpandoObject?所以我可以合并它们(转换为IDictionary '应该这样做)。如果输出是一个ExpandoObject,那也适用于我的情况。 – davehauser

+0

嘿,你实际上可以*!看看我发布的第二个答案(比我的第一个更好):) – BlueMonkMN

14
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); 
    } 
    } 
} 
+0

再次感谢。用这个玩了一下。你为什么要使'MergedType'具有通用性?它也适用于源对象(静态合并类型合并(对象item1,对象item2)),然后在... item1.GetType()。GetProperties()。。 然后我回到ExpandoObject ,这似乎提供了一个类似的解决方案,但代码较少。 我在我的问题中添加了我的最终解决方案 – davehauser

+0

不,我在发布后意识到这一点,并意识到我最初尝试制作更强大类型的解决方案 – BlueMonkMN

+0

然而,如果你有任何理由仍然希望能够通过MergedType访问T1和T2的成员,那么如果你保留了这些模板,你可能会这样做。Intellisense可能会向您展示如果您创建了一个类型为MergedType的类型为T1的属性,并且它的成员是 – BlueMonkMN

2

以下在.NET 3.5(也可能是2.0)下工作。我修改了davehauser的答案。

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