【问题标题】:merging two objects in C#在 C# 中合并两个对象
【发布时间】:2012-01-02 15:36:19
【问题描述】:

我有一个具有各种属性的对象模型 MyObject。在某一时刻,我有两个 MyObject 实例:实例 A 和实例 B。如果实例 B 具有非空值,我想复制实例 A 中的属性并将其替换为实例 B 的属性。

如果我只有 1 个具有 3 个属性的类,没问题,我可以轻松地对其进行硬编码(这是我开始做的)。但我实际上有 12 个不同的对象模型,每个模型大约有 10 个属性。

有什么好的方法可以做到这一点?

【问题讨论】:

标签: c# .net


【解决方案1】:

更新 如果您需要大量调用此方法,请改用AutoMapper。 Automapper 使用Reflection.Emit 构建动态方法,并且会比反射快得多。'

您可以使用反射复制属性的值:

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

我已将其设为通用以确保类型安全。如果你想包含私有属性,你应该使用 Type.GetProperties() 的覆盖,指定绑定标志。

【讨论】:

  • 好的,这看起来就是我需要的。使用反射是否会降低性能?
  • 他只复制非空属性的要求怎么样?
  • 是的,性能损失相当大,据这个人说慢了大约 15 倍:manuelabadia.com/blog/…
  • 请记住,上面的代码不会复制字段,只会复制属性。
  • 是的,反射像糖蜜一样缓慢。但是,现代计算机仍然非常快。所以,试一试,仅当此解决方案导致一些明显的性能问题时才寻找其他解决方案。
【解决方案2】:

我已经尝试过 Kyle Finley 的 Merge Two Objects into an Anonymous Type,它运行良好。

使用TypeMerger,合并就像

一样简单
var obj1 = new {foo = "foo"};

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

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

这就是你得到了合并的对象,除此之外,还有一个忽略特定属性的规定。你也可以对 MVC3 使用同样的东西。

【讨论】:

    【解决方案3】:

    您可以使用反射来做到这一点,但正如有人所说,它会降低性能。

    由于您正在使用预期的类设计,您可以通过使用如下扩展方法来实现相同的目标:

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

    稍后,在您的代码中的某处:

    someInstanceOfMyClass.Merge(someOtherInstanceOfMyClass);
    

    最终,您已将此操作集中在扩展方法中,如果您添加或删除类的属性,您只需要修改扩展方法的实现即可完成所有工作。

    【讨论】:

    • 这仍然会导致OP提到的可维护性问题
    • @BasB 我不确定这一点,因为正如我在回答中所写的,这是一个可预测的设计,我怀疑这个类会随着时间的推移添加或删除大量属性。因此,正如我所说,归根结底,它与使用反射进行操作具有相同的效果,而没有性能损失。
    • @BasB 此外,这些可维护性问题可以使用 Visual Studio 中的 T4 模板解决,因此删除或添加属性可以触发代码生成过程并重新创建此扩展方法。为什么不? WCF 和许多 .NET 框架正在使用代码生成解决这个问题:)
    【解决方案4】:

    你可以使用这个包:XASoft

    使用 foo.Merger(bar) 方法 将 2 个对象组合成一个动态对象

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

    哎呀,看起来像 javascript 语言...

    btw,source code click here

    【讨论】:

      【解决方案5】:

      我为此编写了自己的课程:ObjectMerger。

      基本上它使用反射(因此可能会很慢)。它还包含更多功能,例如解析循环引用的对象并合并它们。我的 ObjectMerger 还包含处理更复杂类的机制,例如 Delegate 或 MemberInfo。这些将被完全复制,类中的其他对象被递归合并。

      语法如下:

      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"
      

      很抱歉,它不能按原样使用,因为由于我公司的限制,目前存储库中缺少一些外部库,但它们很容易被重写。我希望将来可以添加它们。

      【讨论】:

        【解决方案6】:

        这与 @Bas Answer 相同,但用于合并 2 个对象 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);
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-11
          • 2013-01-28
          • 2014-11-29
          • 2021-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多