【问题标题】:Merging anonymous types合并匿名类型
【发布时间】:2011-10-08 22:59:00
【问题描述】:

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

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

【问题讨论】:

  • 如果您使用 C# 4,请使用 dynamic 创建具有动态成员的对象。
  • @Dave,你能发布你想出的方法作为答案吗?会更显眼。我喜欢它,我想投票。 :)
  • @BrunoLM,刚刚发布了我的解决方案。去投票吧:-)

标签: c# anonymous-types


【解决方案1】:

这就是我最终想出的(灵感来自@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;
}

【讨论】:

  • 您可能需要在您的 GetProperties() 调用中添加:.Where(x =&gt; x.CanRead),以避免出现只写属性的情况。
  • @davehauser,你觉得性能好吗?我想每页做几次。
  • @Bomboca 它仍然值得CanRead,因为该方法的签名不会阻止非匿名对象。
  • 有vb.net版吗?
【解决方案2】:
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);
     }
  }
}

【讨论】:

  • 再次感谢。玩了一下这个。有什么理由让MergedType 通用?对于源代码(static MergedType Merge(object item1, object item2)... fi in item1.GetType().GetProperties()),它也适用于 object。然后我回到 ExpandoObject,它似乎提供了类似的解决方案,但代码更少。我在我的问题中添加了我的最终解决方案。
  • 不,我在发帖后正在考虑这个问题,并意识到我最初尝试制作更强类型的解决方案是徒劳的,并且可能完全没有必要使用模板。
  • 但是,如果您有任何理由仍然希望能够通过 MergedType 访问 T1 和 T2 的成员,您可能会在保留模板的情况下做到这一点。如果您创建了一个,Intellisense 可能会告诉您 MergedType 有一个类型为 T1 的属性,以及它的成员是什么。
【解决方案3】:

如果您期望的是单个对象,您不能在其中以编译时类型安全的方式访问属性(而不是在执行时纯粹动态的结果)。您最接近的可能是:

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

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

请记住,匿名类型是在编译时创建的。这不像它们是“动态”类型。为此,您可以在 .NET 4 中使用 ExpandoObject,但它与包含所有相关属性的匿名类型并不完全相同。

【讨论】:

  • 所以,这就是为什么我没有在你的书中找到答案... :-)
  • 是否可以将匿名类型转换为 ExpandoObject?所以我可以合并它们(就像IDictionary&lt;string, object&gt; 应该做的那样)。如果输出是一个 ExpandoObject,那也适用于我的情况。
  • 嘿,其实你可以!看看我发布的第二个答案(比我的第一个好得多):)
  • 哦,我现在看到 ExpandoObject 与我发现的 DynamicObject 非常相似(而且更合适)。
  • 好的,我选择了 BlueMonkMN 的答案。尽管 Jon 正确回答了我的具体问题,但 BlueMonkMN 引导我找到了一个可行的解决方案。
【解决方案4】:

以下适用于 .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;
    }

【讨论】:

    猜你喜欢
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    相关资源
    最近更新 更多