【问题标题】:Creating a copy of an object in C# [duplicate]在 C# 中创建对象的副本 [重复]
【发布时间】:2011-09-28 00:13:06
【问题描述】:

请看下面的代码(摘自一本 C# 书籍):

public class MyClass 
{
    public int val;
}
public struct myStruct 
{
    public int val;
}
public class Program 
{
    private static void Main(string[] args) 
    {
        MyClass objectA = new MyClass();
        MyClass objectB = objectA;

        objectA.val = 10;
        objectB.val = 20;

        myStruct structA = new myStruct();
        myStruct structB = structA;

        structA.val = 30;
        structB.val = 40;

        Console.WriteLine("objectA.val = {0}", objectA.val);
        Console.WriteLine("objectB.val = {0}", objectB.val);
        Console.WriteLine("structA.val = {0}", structA.val);
        Console.WriteLine("structB.val = {0}", structB.val);

        Console.ReadKey();
    }
}

我知道它会产生以下输出:

objectA.val = 20
objectB.val = 20
structA.val = 30
structB.val = 40

输出的最后两行我没有问题,但前两行告诉我objectAobjectB 指向同一个内存块(因为在C# 中,对象是引用类型)。

问题是如何使objectB 成为objectA 的副本,以便它指向内存中的不同区域。我知道尝试分配他们的成员可能不起作用,因为这些成员也可能是参考。那么我该如何让objectB 成为与objectA 完全不同的实体呢?

【问题讨论】:

  • 这可能会有所帮助:stackoverflow.com/questions/129389/…
  • 字符串 json = Newtonsoft.Json.JsonConvert.SerializeObject(myobject); myObjType rCopy = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
  • @hal9000 我使用了序列化/反序列化方法。下面的克隆解决方案有缺点吗?似乎是一个直截了当的解决方案。
  • 我没有遇到任何不利因素。但是我不能评论下面的克隆解决方案。我没试过
  • @SiavashGhanbari 那肯定没有帮助。这个问题仍然悬而未决。此特定问题已被标记为重复。这个问题是重复的。

标签: c# object reference cloning


【解决方案1】:

没有内置方法。您可以让 MyClass 实现 IClonable 接口(但它已被弃用)或者只是编写您自己的 Copy/Clone 方法。无论哪种情况,您都必须编写一些代码。

对于大型对象,您可以考虑序列化 + 反序列化(通过 MemoryStream),只是为了重用现有代码。

无论采用何种方法,请仔细考虑“副本”的确切含义。它应该走多深,是否有要排除的 Id 字段等。

【讨论】:

  • 很抱歉,我知道这是一篇很老的帖子,但您能否提供一个指向已弃用 ICloneable 的文档的链接?我查看了 .net 4.5 的文档,ICloneable 没有说任何关于被弃用的信息。如果是的话,我想用别的东西。
  • 没关系,我找到了。在.NET documentation 中,他们“建议不要在公共 API 中实现 ICloneable”。所以它没有被弃用,只是不推荐使用。
  • @LeeTaylor Deprecated 表示“表示不赞成”,在软件的上下文中,deprecated 表示完全反对使用。您不能拥有一个在公共而不是私有时被弃用的 API。已弃用的 API 已弃用,除非绝对必要,否则根本不应使用。 Microsoft 并没有说你绝对不应该使用它,因为它已被弃用(通常提供替代方案),它只是说避免在公共 API 中使用它。
  • @LeeTaylor Wikipedia 说“弃用是一种应用于计算机软件功能、特征或实践的属性,表明应该避免使用它(通常是因为它被取代了)”;避免就像完全避免,而不是基于可访问性而避免。
  • @LeeTaylor 最后一点,Microsoft 将Obsolete 属性添加到他们认为已弃用的类、结构、枚举、委托、方法、构造函数、属性、字段、事件和接口,这会导致编译器发出CS0618 警告,表明它确实已被弃用。 ICloneable 没有应用 Obsolete 属性。
【解决方案2】:

已经有一个关于这个的问题,你也许可以阅读它

Deep cloning objects

没有 Clone() 方法,因为它存在于 Java 中,但你可以在你的类中包含一个复制构造函数,这是另一种好方法。

class A
{
  private int attr

  public int Attr
  {
     get { return attr; }
     set { attr = value }
  }

  public A()
  {
  }

  public A(A p)
  {
     this.attr = p.Attr;
  }  
}

这是一个示例,在构建新对象时复制成员“Attr”。

【讨论】:

    【解决方案3】:

    你可以这样做:

    class myClass : ICloneable
    {
        public String test;
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }
    

    那你就可以了

    myClass a = new myClass();
    myClass b = (myClass)a.Clone();
    

    注意MemberwiseClone() 创建当前 System.Object 的浅表副本。

    【讨论】:

    • -1 未提及浅/深克隆及其影响。如果没有这些信息,MemberwiseClone() 会很棘手。
    • -1 没有提到 ICloneable 已被弃用,不应使用。
    • @Tom :-1 对您的评论(如果可以的话)未提供有关您的声明的任何链接,因为 ICloneable 在official doc 中未标记为已过时)。您更多的是谈论最佳实践,而不是官方弃用状态。
    • ... 但这会给出与用户原始问题相同的结果。直接来自文档:“如果字段是值类型,则执行字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其克隆指的是同一个对象。” msdn.microsoft.com/en-us/library/…
    • 嗯.. 它不被弃用,因此建议不要在公共 API 中使用,因为该 API 的消费者的实现不清楚,但对于内部使用或开源,我认为没关系。他们说的是“由于 Clone 的调用者不能依赖于执行可预测克隆操作的方法,我们建议不要在公共 API 中实现 ICloneable。”
    【解决方案4】:

    最简单的方法是在 MyClass 类中编写一个复制构造函数。

    类似这样的:

    namespace Example
    {
        class MyClass
        {
            public int val;
    
            public MyClass()
            {
            }
    
            public MyClass(MyClass other)
            {
                val = other.val;
            }
        }
    }
    

    第二个构造函数简单地接受一个他自己类型的参数(你要复制的那个)并创建一个新的对象,分配了相同的值

    class Program
    {
        static void Main(string[] args)
        {
            MyClass objectA = new MyClass();
            MyClass objectB = new MyClass(objectA);
            objectA.val = 10;
            objectB.val = 20;
            Console.WriteLine("objectA.val = {0}", objectA.val);
            Console.WriteLine("objectB.val = {0}", objectB.val);
            Console.ReadKey();
        }
    }
    

    输出:

    objectA.val = 10
    
    objectB.val = 20               
    

    【讨论】:

    • 这对学习很有好处。不是为了练习。
    • 输出的第二行应该是objectB.val = 20,无法编辑,因为编辑太小了
    • 复制构造函数的问题是,如果添加/删除字段,还必须修改复制构造函数。这可能成为维护的噩梦。特别是对于具有许多字段的对象(50+,即 DataContract)。
    • 我喜欢这个答案。 Clone 方法返回类对象,因此它是一种构造函数。那么,为什么叫它克隆而不使用类名呢?那么,何为“维修噩梦”?如果您需要维护大量字段,这是最好的选择。关于“弃用”:没有人强迫您从 IClonable 接口继承。您可以在没有接口的情况下实现 Clone() 方法。但毕竟我会在我的项目中实现复制构造函数。
    • 我同意@Roland。使用 new 关键字,您肯定会创建一个具有不同地址的新对象。我看不出有什么问题。迷恋你可能更喜欢使用反射?
    猜你喜欢
    • 1970-01-01
    • 2016-08-08
    • 1970-01-01
    • 2013-05-17
    • 2020-03-03
    • 1970-01-01
    相关资源
    最近更新 更多