在.NET中,DataContract属性和Serializable属性之间的区别是什么?

18

我正在尝试使用以下方法创建一个对象的深度克隆。

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

这种方法需要一个被序列化的对象,即一个带有“Serializable”属性的类对象。我有一个带有“DataContract”属性的类,但是该方法无法使用该属性。我认为“DataContract”也是一种类型的序列化程序,但可能与“Serializable”不同。
请问有人能告诉我两者之间的区别吗?同时,请让我知道是否可能创建一个深层克隆仅具备1个属性,它可以执行“DataContract”和“Serializable”属性的工作,或者也许还有其他创建深层克隆的方法?
请帮忙!
3个回答

28

感谢@Oded的回复。这里只有一个问题。这个类正在我的WCF服务中使用。我通过添加服务的服务引用在我的消费者项目中使用此服务。每当服务发生变化时,我需要更新服务引用。当我同时使用这两个属性并更新服务时,.net会在服务引用的Reference.cs类中创建两个具有相同名称的属性。分别为“DataMember”和“Serializable”属性。这会导致构建错误。有什么解决方法吗? - samar
@samar - 就我所知,如果使用DataContractSerializable将被忽略。我从未听说过你遇到的问题。 - Oded

6

DataContract是在WCF中使用的,因此需要.NET 3.0+版本。在.NET 2.0或更低版本中没有DataContract和DataMember属性,只有Serializable

正如Oded所说,如果要使用BinaryFormatter,则必须使用Serializable修饰该类型。


3

我曾经通过反射检查对象结构来查找序列化所需的所有程序集,并将它们与序列化一起用于引导。

通过一些工作,可以构建一个类似的方法来进行深拷贝。基本上,需要一个递归方法,带有一个字典来检测循环引用。在方法内部,您可以像这样检查所有字段:

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 等)的情况下,才能使用序列化来实现最后一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接