Как глубоко скопировать класс, не помечая его как Сериализуемый - PullRequest
17 голосов
/ 30 марта 2010

Дан следующий класс:

class A
{
    public List<B> ListB;

    // etc...
}

где B - это другой класс, который может наследовать / содержать некоторые другие классы.


Учитывая этот сценарий:

  1. A - большой класс и содержит много ссылочных типов
  2. Я не могу пометить B как [Serializable], поскольку у меня нет доступа к исходному коду B

Следующие методы для глубокого копирования не работают:

  1. Я не могу использовать ICloneable или MemberwiseClone, поскольку класс A содержит много ссылочных типов
  2. Я не могу написать конструктор копирования для A, так как класс большой и постоянно добавляется, и содержит классы (например, B), которые нельзя глубоко копировать
  3. Я не могу использовать сериализацию, так как не могу пометить содержащийся класс (например, B, где нет исходного кода) как [Serializable]

Как я могу глубоко скопировать класс A?

Ответы [ 7 ]

8 голосов
/ 30 марта 2010

Я прекратил использовать сериализацию для глубокого копирования в любом случае, потому что недостаточно контроля (не каждый класс нужно копировать одинаково) Затем я начал реализовывать свои собственные интерфейсы глубокого копирования и копировать каждое свойство так, как оно должно быть скопировано.

Типичные способы копирования ссылочного типа:

  • использовать конструктор копирования
  • использовать фабричный метод (например, неизменяемые типы)
  • используйте свой собственный "клон"
  • копировать только ссылку (например, другой Root-Type)
  • создание нового экземпляра и копирование свойств (например, типы, не написанные вами без конструктора копирования)

Пример:

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 голосов
/ 01 апреля 2012

Вы можете попробовать это. У меня работает

    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 в коде проекта.

1 голос
/ 15 февраля 2013
    private interface IDeepCopy<T> where T : class
    {
        T DeepCopy();
    }

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

Pluss: Yoy может контролировать процесс копирования (если у вашего класса есть свойство идентификатора, вы можете установить их или вы можете написать другой код бизнес-логики)


Минус: класс можно пометить как запечатанный


0 голосов
/ 29 марта 2017

Попробуйте использовать поток памяти для получения глубокой копии вашего объекта:

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

Вот еще.

0 голосов
/ 14 октября 2015

Ответ из другого потока о том, что использование сериализации json - лучшее, что я видел.

public static T CloneJson<T>(this T source)
{      
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }    
    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}
0 голосов
/ 09 сентября 2010

ваш интерфейс 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(); }
}
0 голосов
/ 30 марта 2010

Разве вы не можете это сделать?

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

А затем обратитесь к Как сделать глубокую копию объекта в .NET (особенно C #)? для клонированияфункция

...