复制一个类,C#

时间:2009-06-23 07:02:53

标签: c#

有没有办法在C#中复制一个类?像var dupe = MyClass(original)。

12 个答案:

答案 0 :(得分:37)

您可能正在谈论深层副本(deep copy vs shallow copy)?

你要么:

  1. 实现(硬编码)你自己的方法,
  2. 尝试实现(或查找)使用Reflection或Emit动态执行的实现(解释here),
  3. 如果对象标有[Serializable]属性,则使用序列化和反序列化来创建深层副本。
  4. public static T DeepCopy<T>(T other)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, other);
            ms.Position = 0;
            return (T)formatter.Deserialize(ms);
        }
    }
    

    要获得浅层副本,可以使用Object.MemberwiseClone()方法,但它是受保护的方法,这意味着您只能在类中使用它。

    使用所有深层复制方法,重要的是要考虑对其他对象的引用,或者循环引用,这可能会导致创建比您想要的更深的副本。

答案 1 :(得分:19)

并非所有课程都具有此功能。可能,如果一个类,它提供Clone方法。为了帮助为您自己的类实现该方法,MemberwiseClone中定义了System.Object受保护方法,该方法生成当前实例的浅表副本(即复制字段;如果它们是引用类型,则引用将指向到原来的位置)。

答案 2 :(得分:8)

如果你的班级刚刚获得了属性,你可以这样做:

SubCentreMessage actual;
actual = target.FindSubCentreFullDetails(120); //for Albany
SubCentreMessage s = new SubCentreMessage();

//initialising s with the same values as 
foreach (var property in actual.GetType().GetProperties())
{
    PropertyInfo propertyS = s.GetType().GetProperty(property.Name);
    var value = property.GetValue(actual, null);
    propertyS.SetValue(s, property.GetValue(actual, null), null);
}

如果您有字段和方法,我相信您可以使用反射在新类中重新创建它们。希望这有帮助

答案 3 :(得分:5)

是和否。这是一个你需要小心的区域,因为有一些陷阱(然后再次,这并不困难)。

首先,如果你稍微在基类库(BCL)中搜索一下,你可能会发现ICloneable interface。并非所有类型都实现此接口,但那些具有Clone方法的接口将返回相同类型的新实例(可能)具有相同的值。

然而,这里有一个陷阱:ICloneable接口没有充分指定是否需要深度克隆或浅层克隆,因此一些实现做了一件事,而其他实现则做另一件事。出于这个原因,ICloneable使用不多,并且不鼓励进一步使用它 - 请参阅优秀的Framework Design Guidelines以获取更多详细信息。

如果深入研究BCL,您可能会发现System.Object具有受保护的MemberwiseClone方法。虽然您无法直接从其他类型调用此方法,但您可以使用此方法在您自己的对象中实现克隆。

常见的克隆模式是定义要克隆的类的受保护构造函数,并将已存在的实例作为参数传递。像这样:

public class MyClass()
{
    public MyClass() {}

    protected MyClass(MyClass other)
    {
        // Cloning code goes here...
    }

    public MyClass Clone()
    {
        return new MyClass(this);
    }
}

但是,这显然只有在您控制要克隆的类型时才有效。

如果您希望克隆无法修改的类型,并且不提供克隆方法,则需要编写代码以将每个数据从旧实例显式复制到新实例。 / p>

答案 4 :(得分:4)

我认为作者正在询问复制构造函数......

答案是“是的,但只有你自己实施”,没有“自动”的方式,没有一些沉重的工作(读:反思):

class MyClass {
    private string _name;

    // standard constructor, so that we can make MyClass's without troubles
    public MyClass(string name) {_name = name}
    public MyClass(MyClass old) {
        _name = old._name;
        // etc...
    }
}

在这方面需要注意的另一件事是IClonable接口和它提供的.Clone()方法。 .MemberwiseClone()类提供的Object受保护方法也有助于实现这两种方法。

答案 5 :(得分:3)

使用JSON怎么样?

您可以从https://www.nuget.org/packages/Newtonsoft.Json/

获取JSON包
using Newtonsoft.Json;
public static List<T> CopyAll<T>(this List<T> list) {
  List<T> ret = new List<T>();
  string tmpStr = JsonConvert.SerializeObject(list);
  ret = JsonConvert.DeserializeObject<List<T>>(tmpStr);
  return ret;
}

答案 6 :(得分:2)

不是任何直截了当的方式。如果您的类为[Serializable]或实现ISerializable,则可以执行往返序列化以创建相同的副本。同样适用于[DataContract]

如果您只想要浅色副本,可以尝试Object.MemberwiseClone()。这是一个受保护的方法,你只能在课堂上使用它。

如果你很幸运,该课程会实现ICloneable,你可以调用Clone()方法。

答案 7 :(得分:1)

一种可能性是clone它。您必须实现接口ICloneableClone方法。

答案 8 :(得分:1)

你的意思是复制构造函数,就像在C ++中存在一样吗?

在C#中不存在。 您可以做的是编写自己的(这是单调乏味的),或者您可以编写一个“克隆”方法,该方法使用序列化来创建一个与原始类具有完全相同值的新实例。

您可以序列化当前实例,对其进行反序列化,然后返回反序列化的结果。缺点是你的类必须是Serializable。

答案 9 :(得分:1)

克隆对象的简单方法是将其写入流并再次读取它:

public object Clone()
{
    object clonedObject = null;
    BinaryFormatter formatter = new BinaryFormatter();
    using (Stream stream = new MemoryStream())
    {
        formatter.Serialize(stream, this);
        stream.Seek(0, SeekOrigin.Begin);
        clonedObject = formatter.Deserialize(stream);
    }

    return clonedObject;
}

但请注意,这会导致所谓的聚合对象出现问题。

答案 10 :(得分:1)

如下:

public class MyClass 
{
    int i;
    double d;
    string something;

    public MyClass(int i, double d) {}

    public MyClass Clone()
    {
        return (new MyClass(i, d));
    }
}

或者,如果您还需要复制构建新对象时通常不知道的其他内容:

    public MyClass CloneMore()
    {
        MyClass obj = new MyClass(i, d);
        obj.something = this.something;
        return (obj);
    }

使用以下方式致电:

MyClass us = them.Clone();

~~~

答案 11 :(得分:1)

这里的一些合理的解决方案序列化是一个有效的方法来解决它以及每个类的克隆方法..鞭打它,它似乎适用于我做的几个测试

using System.Reflection; using System.Text.StringBuilder();

public static T CopyClass2<T>(T obj){
    T objcpy = (T)Activator.CreateInstance(typeof(T));
    obj.GetType().GetProperties().ToList()
    .ForEach( p => objcpy.GetType().GetProperty(p.Name).SetValue(objcpy, p.GetValue(obj)));
    return objcpy;

}

这是我想的更详细的版本,上面建议你理解Lambda表达式并不常见。如果你尊重可读性(这是完全有效的),我会在下面使用

public static T CopyClass<T>(T obj)
{
    T objcpy = (T)Activator.CreateInstance(typeof(T));
    foreach (var prop in obj.GetType().GetProperties())
    {
        var value = prop.GetValue(obj);
        objcpy.GetType().GetProperty(prop.Name).SetValue(objcpy, value);
    }
    return objcpy;

}

使用此方法列出属性以查看它是否复制到新引用...简单它没有列出none基元的属性(结构化类型)..所以你将得到一个类名

public static string ListAllProperties<T>(T obj)
{
        StringBuilder sb = new System.Text.StringBuilder();
        PropertyInfo[] propInfo = obj.GetType().GetProperties();
        foreach (var prop in propInfo)
        {
            var value = prop.GetValue(obj) ?? "(null)";
            sb.AppendLine(prop.Name + ": " + value.ToString());
        }
        return sb.ToString();
}