在C#中,实现接口和应用属性的区别是什么?

21
这可能是一个愚蠢的问题,但我还是要问一下,
我正在阅读Jim Keogh和Mario Giannini的《OOP Demystified: A Self-Teaching Guide》第11章,其中介绍了接口。该书中的示例为C++。
我注意到C++使用ISerializable使类可序列化,您需要实现它,而在C#中,您只需将类属性标记为[Serializable]即可。
这里的关键区别是什么?是否意味着使用接口必须提供实现,而如果标记某些东西,则编译器将为您解决实现?
我猜想,使用[Serializable]属性,.Net框架使用反射从实际对象创建序列化对象。
话虽如此,在这种情况下是否可以有一个[Disposable]属性或者按照我的理论,框架不知道如何处理对象的处理,因此您必须自己处理?
感谢解释。

2
我认为这是一个很好的问题。 - James Westgate
6个回答

11
很久很久以前,在遥远的星系里...没有属性或编译器支持类元数据,所以开发人员尝试实现自己的方法。我们祖先之一找出来的方法是声明标记接口(Marker Interfaces)
因此,回答你的问题:自定义属性是标记接口的“进化”。两者都可以使用。但请注意,如果要强制要求对象实现特定方法,则使用接口,简单明了。这就是IDisposable的工作原理,它强制你实现一个名为Dispose()的方法。 [Serializable](可能在您的C++示例中还有ISerializable)不强制您实现任何内容,因为运行时将仅读取该声明并执行其任务(即,序列化对象)。
请注意,C#也有ISerializable接口...它旨在让您编写自定义序列化代码,然后由运行时调用。请注意,它不是标记接口,也不能替代[Serializable]属性,因为您仍然需要标记类属性才能进行序列化。

4
"标记接口" 在泛型和扩展方法中仍然非常实用。它们允许将接口用作泛型约束,这是使用属性无法实现的。 - Matthew Whited
1
@Matthew:没错,我认为这正是ISerializable接口存在的原因。 - Philip Daubmeier
3
ISerializable 接口允许您实现自定义方法来代替使用默认的反射序列化过程。至于我所知道的,在 .Net 中唯一的标记接口是 INamingContainer ,用于 ASP.Net。 - Matthew Whited
1
ISerializable不是一个标记接口。来自MSDN的“允许对象控制其自身的序列化和反序列化”。 - James Westgate

7

属性通常为类型或成员提供附加的元数据;有一些重要限制(如const值等),Eric Lippert提供了一些关于接口和属性之间差异的想法,这可能很有启发性。

接口还有其他一些方面:

  • 它们可以有多个成员
  • 接口背后有一些实现(这很关键)
  • 您可以使用接口进行抽象(而不是属性)

然而,缺点是一旦一个类型实现了一个接口,所有子类型也通过继承实现该接口。与属性相比,属性可以被继承,但不希望被继承。

仅因为 Foo 是可序列化的,并不意味着 Bar (:Foo) 必须是可序列化的;因此,在每个级别上定义这一点是很好的——尽管实际上我认为 BinaryFormatter 不应该是 mots 序列化代码的关键部分(虽然我会闭嘴)。
实际上,如果您检查 IL,您会发现 [Serializable] 实际上并没有被写入作为属性——它是一个 CLI 标志(一些编译器魔法)。但这并不改变事实。
如果您只需要表达元数据(有关类型/成员的事实),则属性是理想的。如果您需要表达行为/API,则使用接口。

6

3
并不完全正确。某些特定的属性,比如 [Conditional],会在编译时进行检查。但你不能自己实现需要在编译时被检查的属性(除非使用像 PostSharp 这样的工具)。 - Aaronaught

4

我认为您忽略了一个事实,.NET(C#)也有一个ISerializable接口:

[Serializable]
class Foo : ISerializable
{
}

属性是“自定义元数据”,接口是一种语言特性。

2
您对属性的理解过于深入。 [Serializable] 的作用非常有限,它仅用于二进制序列化,由 BinaryFormatter 类实现。该类具有非常强大的功能,它可以创建一个类的实例,而不使用类的公共属性。它直接分配值给标记为私有的字段,完全绕过了正常的访问规则。
您必须明确授予 BinaryFormatter 这样做的权利,基本上是承认您的类的对象可以被无问题地反序列化。您可以通过应用 [Serializable] 属性来实现这一点。BinaryFormatter 仅检查其是否存在。就这些。

强大 = 混淆、模棱两可、不可预测。 - James Westgate

1

我认为这是因为传统上某些属性已被标记为可序列化或不可序列化,这意味着在类级别也具有该属性更有意义。

在运行时检查类的属性与在编译时检查类型相比会略微降低性能。


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