如何深度复制类而不将其标记为Serializable

时间:2010-03-30 12:22:49

标签: c# deep-copy cloning

鉴于以下课程:

class A
{
    public List<B> ListB;

    // etc...
}

其中B是另一个可以继承/包含其他类的类。


鉴于这种情况:

  1. A是一个大型类,包含许多引用类型
  2. 我无法将B标记为[Serializable],因为我无法访问B
  3. 的源代码

    以下执行深度复制的方法不起作用:

    1. 我无法使用ICloneableMemberwiseClone,因为类A包含许多引用类型
    2. 我无法为A编写复制构造函数,因为该类很大且不断被添加到,并且包含无法深度复制的类(如B
    3. 我无法使用序列化,因为我无法标记包含的类(如B,其中没有可用的源代码)[Serializable]

    4. 如何深度复制课程A

7 个答案:

答案 0 :(得分:8)

无论如何我都停止使用序列化进行深度复制,因为没有足够的控件(并非每个类都需要以相同的方式复制)。然后我开始实现自己的深层复制接口,并以应该复制的方式复制每个属性。

复制引用类型的典型方法:

  • 使用复制构造函数
  • 使用工厂方法(例如,不可变类型)
  • 使用您自己的“克隆”
  • 仅复制参考(例如,其他根类型)
  • 创建新实例和复制属性(例如,类型不是由您自己编写,缺少复制构造函数)

示例:

class A
{
  // copy constructor
  public A(A copy) {}
}

// a referenced class implementing 
class B : IDeepCopy
{
  object Copy() { return new B(); }
}

class C : IDeepCopy
{
  A A;
  B B;
  object Copy()
  {
    C copy = new C();

    // copy property by property in a appropriate way
    copy.A = new A(this.A);
    copy.B = this.B.Copy();
  }
}

你可能认为这是一项巨大的工作。但最后,它很简单直接,可以在需要的地方进行调整,并完全满足您的需求。

答案 1 :(得分:3)

你可以试试这个。它对我有用

    public static object DeepCopy(object obj)
    {
        if (obj == null)
            return null;
        Type type = obj.GetType();

        if (type.IsValueType || type == typeof(string))
        {
            return obj;
        }
        else if (type.IsArray)
        {
            Type elementType = Type.GetType(
                 type.FullName.Replace("[]", string.Empty));
            var array = obj as Array;
            Array copied = Array.CreateInstance(elementType, array.Length);
            for (int i = 0; i < array.Length; i++)
            {
                copied.SetValue(DeepCopy(array.GetValue(i)), i);
            }
            return Convert.ChangeType(copied, obj.GetType());
        }
        else if (type.IsClass)
        {

            object toret = Activator.CreateInstance(obj.GetType());
            FieldInfo[] fields = type.GetFields(BindingFlags.Public |
                        BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;
                field.SetValue(toret, DeepCopy(fieldValue));
            }
            return toret;
        }
        else
            throw new ArgumentException("Unknown type");
    }

感谢代码项目中的DetoX83 article

答案 2 :(得分:1)

    private interface IDeepCopy<T> where T : class
    {
        T DeepCopy();
    }

    private class MyClass : IDeepCopy<MyClass>
    {
        public MyClass DeepCopy()
        {
            return (MyClass)this.MemberwiseClone();
        }
    }

Pluss: Yoy可以控制复制过程(如果您的类具有可以设置它们的标识符属性,或者您可以编写其他业务逻辑代码)


减:类可以标记为密封


答案 3 :(得分:0)

你不能这样做吗?

[Serializable]
class A
{
     ...
    [NonSerialized]
    public List<B> ListB;
    ....
}

然后参考How do you do a deep copy of an object in .NET (C# specifically)?获取克隆函数

答案 4 :(得分:0)

您的界面IDeepCopy正是ICloneable指定的内容。

class B : ICloneable
{
     public object Clone() { return new B(); }
}

以及更友好的实施:

class B : ICloneable
{
     public B Clone() { return new B(); }
     // explicit implementation of ICloneable
     object ICloneable.Clone() { return this.Clone(); }
}

答案 5 :(得分:-1)

来自使用json序列化的其他线程的answer是我见过的最好的。

public static T CloneJson<T>(this T source)
{      
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }    
    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}

答案 6 :(得分:-1)

尝试使用内存流来获取对象的深层副本:

 public static T MyDeepCopy<T>(this T source)
            {
                try
                {

                    //Throw if passed object has nothing
                    if (source == null) { throw new Exception("Null Object cannot be cloned"); }

                    // Don't serialize a null object, simply return the default for that object
                    if (Object.ReferenceEquals(source, null))
                    {
                        return default(T);
                    }

                    //variable declaration
                    T copy;
                    var obj = new DataContractSerializer(typeof(T));
                    using (var memStream = new MemoryStream())
                    {
                        obj.WriteObject(memStream, source);
                        memStream.Seek(0, SeekOrigin.Begin);
                        copy = (T)obj.ReadObject(memStream);
                    }
                    return copy;
                }
                catch (Exception)
                {
                    throw;
                }
            }

Here is more.