【问题标题】:Why is the C# SerializedAttribute is sealed?为什么 C# SerializedAttribute 是密封的?
【发布时间】:2013-01-22 18:49:00
【问题描述】:

我试图创建一个暗示 [Serializable] 的属性,但我注意到这个 SerializableAttribute 类是密封的。

在 Java 中,可以创建一个继承自 Serializable 接口的接口(例如,MyInterface),因此MyInterface 的所有子类也可以序列化,甚至它的子子类也是如此.

假设我正在创建一个 ORM,并且我希望客户将他们的实体类注释为 [DatabaseEntity],但为了确保实体是可序列化的,我还需要要求他们使用额外的 [Serializable] 来为其类添加属性看起来不太紧凑和整洁。

我想知道为什么 SerializableAttribute 类是 sealed 以及为什么有 Inherited=false 这意味着可序列化类的子类将不可序列化,除非它明确表示。这些设计选择背后的动机是什么?

【问题讨论】:

  • 您不需要serializable 来使类可序列化。它仅由 BinaryFormatter 使用。其他序列化器可以愉快地序列化您的 ex、xmlSerializer、DatacontractSerializer、DatacontractJsonSerializer、JavascriptSerializer 等类。
  • 但我希望我的课程同时从myObject.getType().IsSerializable 返回true。如果您认为是这样,请添加您的方法作为答案。
  • 你需要什么myObject.getType().IsSerializable。默认情况下,每个类都是可序列化的(与serializable 属性无关)。
  • Ahmet,如果您只是想要一个标有“可序列化”的标记,那么实现 ISerializable 并测试它是否存在。我不知道,也许你有一些你想要允许序列化的类,而你没有,你想要一种方便的方式来区分它们。

标签: c#


【解决方案1】:

SerializableAttribute 仅由 BinaryFormatter 使用。如果您正在编写自己的序列化程序,请不要担心。

sealed 关键字应用于属性而不是与属性关联的类。就是说 SerializableAttribute 不能被子类化。

BinaryFormatter 使用选择加入模型。任何类(或子类)都必须指定它是可序列化的。这就是使用Inherited=false 的原因。

【讨论】:

  • 那么 C# 对[Serializable]-tagged 类的不可序列化成员有编译时错误机制吗?
  • 字段/属性有[NonSerializable]。此外,一个类可以实现ISerializable 来搞砸。同样,这仅适用于 BinaryFormatter。
【解决方案2】:

建议的最佳实践是所有 .Net 属性都应该被密封,according to Microsoft:

.NET Framework 类库提供检索自定义属性的方法。默认情况下,这些方法搜索属性继承层次结构;例如System.Attribute.GetCustomAttribute 搜索指定的属性类型,或扩展指定属性类型的任何属性类型。 密封属性消除了通过继承层次结构的搜索,可以提高性能。 [我的重点]

所以[Serializable] 是密封的,因为.Net 反射检查属性更快。代价是你不能继承和扩展SerializableAttribute

如果需要,您可以创建自己的未密封属性(但您会收到代码分析警告)。

这有点让人困惑,属性是如何在继承中用于它们所应用的类的。最好举个例子:

[Serializable]
public class A
{
    public int SimpleSerialisableProperty { get; set;}
}

public class B : A
{
    public C ComplexReferenceProperty { get; set; }
}

[Serializable]
public class D : A
{
    public bool AnotherSerialisableProperty { get; set;}
}

你问为什么SerializableAttribute.Inherited = false,这就是为什么:

  • A 类被标记为[Serializable],它是。

  • 但是B 类继承A 并使用不可序列化的属性对其进行扩展。如果 .Net 尝试序列化 B 将会遇到错误。

  • Inherited = false 告诉 .Net,仅仅因为 A 已被标记为 [Serializable] 并不是每个继承它的类都可以序列化。

  • 现在D 类继承A 并且是可序列化的,因此它拥有自己的[Serializable] 属性。

最后,就设计而言,属性是扩展行为的好方法(属性网格中的漂亮 UI 编辑器等)。然而,他们在执行方面很糟糕。如果您需要您的客户以特定方式实现他们的实体类,那么abstract 基类或interface 是一个更好的方法。如果您将其设为属性,那么您基本上是在让他们知道[Serializable] 是一个您可以处理任何一种方式的选项。

【讨论】:

    【解决方案3】:

    序列化不是一件神奇的事情,你不需要任何属性来序列化一个对象。这是一个将你的类的属性和字段写入流的过程(并且属性只是序列化程序关于在输出对象时如何表现的指令)。

    查看这个过度简化的序列化程序代码,它完全忽略了包括NonSerializable在内的所有属性

    object obj = yourObject;
    
    var props = obj.GetType()
                   .GetProperties()
                   .ToDictionary(p => p.Name, p => p.GetValue(obj, null));
    
    string serializedText = String.Join("\n",
                  props.Select(kv => kv.Key + "=" + kv.Value ?? kv.Value.ToString()));
    

    例如,上面的代码会给出

    IsEmpty=False
    X=3
    Y=5
    

    object obj = new Point(3,5);

    反序列化过程是读取这些值并相应地设置属性。

    【讨论】:

      【解决方案4】:

      将 [Serializable] 属性放在要序列化的类的顶部。序列化是选择加入的过程。您必须为要序列化的每个类手动执行此操作。还有一堆其他的关键字。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        • 2016-06-27
        • 2014-12-27
        • 1970-01-01
        • 2010-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多