discussed before on Stack Overflow我们应该更喜欢属性marker interfaces(没有任何成员的接口)。 Interface Design article on MSDN也宣称这个建议:
避免使用标记接口(没有成员的接口)。
自定义属性提供了一种标记类型的方法。有关自定义属性的更多信息,请参阅编写自定义属性。如果可以在执行代码之前推迟检查属性,则首选自定义属性。如果您的方案需要编译时检查,则无法遵守此准则。
甚至还有FxCop rule来执行此建议:
避免空接口
接口定义提供行为或使用合同的成员。无论类型在继承层次结构中出现何种位置,接口描述的功能都可以采用任何类型。类型通过为接口的成员提供实现来实现接口。空接口不定义任何成员,因此,不定义可以实现的合同。
如果您的设计包含期望实现类型的空接口,则可能使用接口作为标记,或者标识一组类型的方法。如果此标识将在运行时发生,则完成此操作的正确方法是使用自定义属性。使用属性的存在或不存在或属性的属性来标识目标类型。如果标识必须在编译时进行,则可以使用空接口。
本文只说明了一个可能忽略警告的原因:何时需要编译时识别类型。 (这与界面设计文章一致)。
如果在编译时使用接口标识一组类型,则可以安全地从此规则中排除警告。
实际问题出在这里:Microsoft在框架类库的设计中(至少在几种情况下)不符合他们自己的建议:IRequiresSessionState interface和IReadOnlySessionState interface。 ASP.NET框架使用这些接口来检查它是否应该为特定处理程序启用会话状态。显然,它不用于类型的编译时识别。他们为什么不这样做?我可以想到两个可能的原因:
微优化:检查对象是否实现接口(obj is IReadOnlySessionState
)比使用反射检查属性(type.IsDefined(typeof(SessionStateAttribute), true)
)要快。大多数时候差异可以忽略不计,但它实际上可能对ASP.NET运行时中的性能关键代码路径很重要。但是,他们可以使用的解决方法就像为每个处理程序类型缓存结果一样。有趣的是,ASMX Web服务(具有类似性能特征)实际上使用EnableSession
property的WebMethod
attribute来实现此目的。
与使用第三方.NET语言的属性装饰类型相比,实现接口的可能性更大。由于ASP.NET被设计为与语言无关,并且ASP.NET在CodeDom的帮助下生成类型的代码(可能是第三方语言),它们基于EnableSessionState
实现所述接口<%@ Page %>
directive的属性,使用接口而不是属性可能更有意义。
使用标记界面而不是属性的有说服力的理由是什么?
这只是一个(过早?)优化还是框架设计中的一个小错误? (他们是否认为reflection is a "big monster with red eyes"?)想法?
答案 0 :(得分:14)
我通常避免使用“标记接口”,因为它们不允许取消标记派生类型。但除此之外,以下是我看到的一些特定情况,其中标记接口比内置元数据支持更可取:
答案 1 :(得分:10)
对于泛型类型,您可能希望在标记接口中使用相同的泛型参数。这是属性无法实现的:
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的类来处理。
答案 2 :(得分:6)
微软在制作.NET 1.0时没有严格遵循这些指导原则,因为指南与框架一起发展,以及他们在更改API之前没有学到的一些规则。
Framework Design Guidelines中解释了这一点。
也就是说,该书还指出“[A]属性测试比类型检查要昂贵得多”(在Rico Mariani的附文中)。
接着说,有时您需要标记接口进行编译时检查,这对于属性是不可能的。但是,我发现书中给出的例子(第88页)令人难以置信,所以我不会在这里重复。
答案 3 :(得分:4)
我非常支持Marker Interfaces。我从不喜欢属性。我将它们视为类和成员的某种元信息,例如供调试者查看。与异常类似,他们不应该以我最谦卑的观点来影响正常的处理逻辑。
答案 4 :(得分:3)
从编码的角度来看,我认为我更喜欢标记界面语法,因为内置关键字as
和is
。属性标记需要更多代码。
[MarkedByAttribute]
public class MarkedClass : IMarkByInterface
{
}
public class MarkedByAttributeAttribute : Attribute
{
}
public interface IMarkByInterface
{
}
public static class AttributeExtension
{
public static bool HasAttibute<T>(this object obj)
{
var hasAttribute = Attribute.GetCustomAttribute(obj.GetType(), typeof(T));
return hasAttribute != null;
}
}
使用代码的一些测试:
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ClassMarkingTests
{
private MarkedClass _markedClass;
[TestInitialize]
public void Init()
{
_markedClass = new MarkedClass();
}
[TestMethod]
public void TestClassAttributeMarking()
{
var hasMarkerAttribute = _markedClass.HasAttibute<MarkedByAttributeAttribute>();
Assert.IsTrue(hasMarkerAttribute);
}
[TestMethod]
public void TestClassInterfaceMarking()
{
var hasMarkerInterface = _markedClass as IMarkByInterface;
Assert.IsTrue(hasMarkerInterface != null);
}
}