Разница между атрибутом DataContract и атрибутом Serializable в.net
Я пытаюсь создать глубокий клон объекта, используя следующий метод.
public static T DeepClone<T>(this T target)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, target);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}
Этот метод требует объект, который сериализуется, т.е. объект класса, который имеет атрибут "Сериализуемый" на нем. У меня есть класс, который имеет атрибут "DataContract", но метод не работает с этим атрибутом. Я думаю, что "DataContract" также является типом сериализатора, но может отличаться от "Serializable".
Может ли кто-нибудь дать мне разницу между этими двумя? Также, пожалуйста, дайте мне знать, если возможно создать глубокий клон объекта только с одним атрибутом, который выполняет работу как атрибутов "DataContract" и "Serializable", или, возможно, другим способом создания глубокого клона?
Пожалуйста помоги!
3 ответа
Serializable
необходим для BinaryFormatter
работать.
DataContract
и DataMember
атрибут используется с DataContractSerializer
,
Вы можете украсить класс атрибутами для обоих сериализаторов.
DataContract
поэтому используется в WCF.NET 3.0+. В.net 2.0 или ниже нет DataContract, DataMember
только атрибут Serializable
,
Как сказал Одед, если вы хотите использовать BinaryFormatter, вы должны украсить тип с помощью Serializable.
Однажды я сделал некоторый осмотр структуры объекта через Reflection, чтобы найти все сборки, необходимые для десериализации, и сериализовать их вместе с начальной загрузкой.
Немного поработав, можно создать аналогичный метод для глубокого копирования. По сути, вам нужен рекурсивный метод, который использует словарь для обнаружения циклических ссылок. Внутри метода вы проверяете все поля примерно так:
private void InspectRecursively(object input,
Dictionary<object, bool> processedObjects)
{
if ((input != null) && !processedObjects.ContainsKey(input))
{
processedObjects.Add(input, true);
List<FieldInfo> fields = type.GetFields(BindingFlags.Instance |
BindingFlags.Public | BindingFlags.NonPublic );
foreach (FieldInfo field in fields)
{
object nextInput = field.GetValue(input);
if (nextInput is System.Collections.IEnumerable)
{
System.Collections.IEnumerator enumerator = (nextInput as
System.Collections.IEnumerable).GetEnumerator();
while (enumerator.MoveNext())
{
InspectRecursively(enumerator.Current, processedObjects);
}
}
else
{
InspectRecursively(nextInput, processedObjects);
}
}
}
}
Чтобы заставить его работать, вам нужно добавить объект вывода и что-то вроде System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type)
создать самую мелкую копию (даже без копирования ссылок) значения каждого поля. Наконец, вы можете установить каждое поле с чем-то вроде field.SetValue(input, output)
Однако эта реализация не поддерживает зарегистрированные обработчики событий, которые такжене поддерживаются десериализацией. Кроме того, каждый объект в иерархии будет поврежден, если конструктор его класса должен что-либо инициализировать, кроме установки всех полей. Последний пункт работает только с сериализацией, если класс имеет соответствующую реализацию, например, помеченный метод [OnDeserialized]
, реализует ISerializable
...