Подтвердить что ты не робот

Как глубоко скопировать класс, не отмечая его как Serializable

Учитывая следующий класс:

class A
{
    public List<B> ListB;

    // etc...
}

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


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

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

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

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

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

4b9b3361

Ответ 1

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

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

  • использовать конструктор копирования
  • использовать метод factory (например, неизменяемые типы)
  • используйте свой собственный "Clone"
  • копировать только ссылку (например, другой 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();
  }
}

Вы можете подумать, что это огромная работа. Но в конце, это легко и прямо, можно настраивать там, где это необходимо, и делает именно то, что вам нужно.

Ответ 2

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

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

Ответ 3

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

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

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


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


Ответ 5

ваш интерфейс 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(); }
}

Ответ 6

Невозможно выполнить сериализацию XML?

Ответ 7

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

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

Подробнее...

Ответ 8

An answer из другого потока, который использует сериализацию json - лучшее, что я видел.

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