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

问题描述 投票:17回答:7

鉴于以下课程:

class A
{
    public List<B> ListB;

    // etc...
}

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


鉴于这种情况:

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

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

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

我怎样才能深入复制班级A

c# deep-copy cloning
7个回答
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();
  }
}

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


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代码项目。


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

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

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


减:课程可以标记为密封



0
投票

你不能这样做吗?

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

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


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

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

-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.

© www.soinside.com 2019 - 2024. All rights reserved.