Как глубоко скопировать класс, не отмечая его как Serializable
Учитывая следующий класс:
class A
{
public List<B> ListB;
// etc...
}
где B
- это еще один класс, который может наследовать/содержать некоторые другие классы.
Учитывая этот сценарий:
-
A
- большой класс и содержит много ссылочных типов
- Я не могу пометить
B
как [Serializable]
, поскольку у меня нет доступа к исходному коду B
Следующие методы для глубокого копирования не работают:
- Я не могу использовать
ICloneable
или MemberwiseClone
, поскольку класс A
содержит много ссылочных типов
- Я не могу написать конструктор копирования для
A
, так как класс большой и постоянно добавляется к нему и содержит классы (например, B
), которые не могут быть глубоко скопированы
- Я не могу использовать сериализацию, поскольку я не могу пометить содержащийся класс (например,
B
, где нет исходного кода) в качестве [Serializable]
Как я могу использовать класс глубокой копии A
?
Ответы
Ответ 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 может управлять процессом копирования (если у вашего класса есть свойство идентификатора, которое вы можете установить, или вы можете написать другой код бизнес-логики)
Минус: класс может быть помечен как запечатанный
Ответ 4
Вы не можете это сделать?
[Serializable]
class A
{
...
[NonSerialized]
public List<B> ListB;
....
}
И затем обратитесь к Как вы делаете глубокую копию объекта в .NET(С# специально)? для функции клонирования
Ответ 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));
}