【发布时间】:2011-01-06 09:15:08
【问题描述】:
discussed before on Stack Overflow 我们应该更喜欢属性而不是marker interfaces(没有任何成员的接口)。 Interface Design article on MSDN 也提出了这个建议:
避免使用标记接口(没有成员的接口)。
自定义属性提供了一种标记类型的方法。有关自定义属性的更多信息,请参阅编写自定义属性。当您可以将属性检查推迟到代码执行时,自定义属性是首选。如果您的方案需要编译时检查,则不能遵守此指南。
甚至还有 FxCop rule 来强制执行此建议:
避免空接口
接口定义了提供行为或使用契约的成员。接口描述的功能可以被任何类型采用,无论该类型出现在继承层次结构中的什么位置。类型通过为接口的成员提供实现来实现接口。空接口没有定义任何成员,因此也没有定义可以实现的合约。
如果您的设计包含类型预期实现的空接口,则您可能正在使用接口作为标记,或者是一种识别一组类型的方法。如果此标识将在运行时发生,则完成此操作的正确方法是使用自定义属性。使用属性的存在与否或属性的属性来识别目标类型。如果必须在编译时进行识别,那么使用空接口是可以接受的。
文章仅说明了您可能会忽略警告的一个原因:当您需要类型的编译时标识时。 (这与界面设计文章一致)。
如果接口用于在编译时识别一组类型,则可以安全地从该规则中排除警告。
真正的问题来了:微软在框架类库的设计中没有遵循他们自己的建议(至少在一些情况下):IRequiresSessionState interface 和IReadOnlySessionState interface。 ASP.NET 框架使用这些接口来检查它是否应该为特定处理程序启用会话状态。显然,它不用于类型的编译时识别。为什么他们不这样做?我能想到两个可能的原因:
-
微优化:检查对象是否实现接口 (
obj is IReadOnlySessionState) 比使用反射检查属性 (type.IsDefined(typeof(SessionStateAttribute), true)) 更快。这种差异在大多数情况下可以忽略不计,但它实际上可能对 ASP.NET 运行时中的性能关键代码路径很重要。但是,他们可以使用一些变通方法,例如为每个处理程序类型缓存结果。有趣的是,ASMX Web 服务(具有相似的性能特征)实际上为此使用了WebMethodattribute 的EnableSessionproperty。 -
第三方 .NET 语言可能比使用属性装饰类型更可能支持实现接口。由于 ASP.NET 被设计为与语言无关,并且 ASP.NET 为基于 @ 的
EnableSessionState属性实现上述接口的类型(可能在 CodeDom 的帮助下使用第三方语言)生成代码987654330@,使用接口而不是属性可能更有意义。
使用标记接口而不是属性的有说服力的理由是什么?
这仅仅是一个(过早的?)优化还是框架设计中的一个小错误? (他们认为reflection is a "big monster with red eyes"?)想法?
【问题讨论】:
-
冒着听起来太尖刻的风险,微软做过什么让你期望他们能做到一致性?多年来,他们提供了许多有用的工具和软件,但我从未从他们身上看到的一件事是一致的行为,或遵守他们自己的准则或规则。
-
@jalf:我同意他们因不遵守自己的准则而臭名昭著,但公平地说,他们在 .NET 方面做得很好。 .NET Framework 表现出大量的一致性,并且通常设计得非常好。
-
一般设计得非常好,是的,而且异常一致,但并不完美。回想起来,有很多愚蠢的小设计错误是显而易见的。但正如 Mark 的回答所说,这些问题在设计 BCL 时可能并不那么明显——因此指南建议也不是。
-
@jalf:有时设计早于指南推荐。
标签: .net asp.net attributes interface marker-interfaces