与marker interfaces (没有任何成员的接口)相比,我们应该更喜欢属性( attributes ),这已经是discussed before on Stack Overflow了。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服务(具有类似的性能特征)实际上使用WebMethod
attribute的EnableSession
property来实现此目的。与第三方.NET语言使用属性修饰类型相比,更有可能支持
<%@ Page %>
directive的EnableSessionState
属性为实现上述接口的类型(可能在CodeDom的帮助下使用第三方语言)生成代码,因此使用接口而不是属性可能更有意义。使用标记接口而不是属性的有说服力的理由是什么?
这仅仅是一种(过早的)吗?是优化还是框架设计中的一个小错误?(他们认为是reflection is a "big monster with red eyes"吗?)有什么想法?
发布于 2010-02-02 04:14:59
我通常避免使用“标记接口”,因为它们不允许您取消对派生类型的标记。但除此之外,下面是我见过的一些特定情况,其中标记接口比内置元数据支持更可取:
使用不支持批注或attributes.
发布于 2010-08-08 07:10:40
对于泛型类型,您可能希望在标记接口中使用相同的泛型参数。这不是通过一个属性可以实现的:
interface MyInterface<T> {}
class MyClass<T, U> : MyInterface<U> {}
class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}
这种接口对于将一种类型与另一种类型关联可能很有用。
标记接口的另一个很好的用法是当您想要创建kind of mixin时
interface MyMixin {}
static class MyMixinMethods {
public static void Method(this MyMixin self) {}
}
class MyClass : MyMixin {
}
acyclic visitor pattern也使用它们。术语“退化接口”有时也会被使用。
更新:
我不知道这个是否算数,但我已经用它们标记了post-compiler要处理的类。
发布于 2010-01-18 22:07:52
微软在制定API1.0时并没有严格遵循这些指导方针,因为这些指导方针是与框架一起演变的,而且他们直到改变.NET时才学习到一些规则已经为时已晚。
IIRC,你提到的例子属于BCL 1.0,这就可以解释它了。
这在Framework Design Guidelines中有解释。
也就是说,这本书还说“属性测试比类型检查的成本要高得多”(在Rico Mariani的侧栏中)。
它继续说,有时你需要标记接口来进行编译时检查,而这对于属性来说是不可能的。然而,我发现书中给出的例子(第88页)并不令人信服,所以我不会在这里重复。
https://stackoverflow.com/questions/2086451
复制相似问题