Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compelling Reasons to Use Marker Interfaces Instead of Attributes

It's been discussed before on Stack Overflow that we should prefer attributes to marker interfaces (interfaces without any members). Interface Design article on MSDN asserts this recommendation too:

Avoid using marker interfaces (interfaces with no members).

Custom attributes provide a way to mark a type. For more information about custom attributes, see Writing Custom Attributes. Custom attributes are preferred when you can defer checking for the attribute until the code is executing. If your scenario requires compile-time checking, you cannot comply with this guideline.

There's even an FxCop rule to enforce this recommendation:

Avoid empty interfaces

Interfaces define members that provide a behavior or usage contract. The functionality described by the interface can be adopted by any type, regardless of where the type appears in the inheritance hierarchy. A type implements an interface by providing implementations for the interface's members. An empty interface does not define any members, and as such, does not define a contract that can be implemented.

If your design includes empty interfaces that types are expected to implement, you are probably using an interface as a marker, or a way of identifying a group of types. If this identification will occur at runtime, the correct way to accomplish this is to use a custom attribute. Use the presence or absence of the attribute, or the attribute's properties, to identify the target types. If the identification must occur at compile time, then using an empty interface is acceptable.

The article states only one reason that you might ignore the warning: when you need compile time identification for types. (This is consistent with the Interface Design article).

It is safe to exclude a warning from this rule if the interface is used to identify a set of types at compile-time.

Here comes the actual question: Microsoft didn't conform to their own recommendation in the design of the Framework Class Library (at least in a couple cases): IRequiresSessionState interface and IReadOnlySessionState interface. These interfaces are used by the ASP.NET framework to check whether it should enable session state for a specific handler or not. Obviously, it's not used for compile time identification of types. Why they didn't do that? I can think of two potential reasons:

  1. Micro-optimization: Checking whether an object implements an interface (obj is IReadOnlySessionState) is faster than using reflection to check for an attribute (type.IsDefined(typeof(SessionStateAttribute), true)). The difference is negligible most of the time but it might actually matter for a performance-critical code path in the ASP.NET runtime. However, there are workarounds they could have used like caching the result for each handler type. The interesting thing is that ASMX Web services (which are subject to similar performance characteristics) actually use the EnableSession property of the WebMethod attribute for this purpose.

  2. Implementing interfaces are potentially more likely to be supported than decorating types with attributes by third-party .NET languages. Since ASP.NET is designed to be language agnostic, and ASP.NET generates code for types (possibly in a third-party language with the help of CodeDom) that implement the said interfaces based on the EnableSessionState attribute of the <%@ Page %> directive, it might make more sense to use interfaces instead of attributes.

What are the persuasive reasons to use marker interfaces instead of attributes?

Is this simply a (premature?) optimization or a tiny mistake in the framework design? (Do they think reflection is a "big monster with red eyes"?) Thoughts?

like image 984
mmx Avatar asked Jan 18 '10 13:01

mmx


3 Answers

I generally avoid "marker interfaces" because they don't allow you to unmark a derived type. But that aside, here are some of the specific cases that I have seen where marker interfaces would be preferable to built-in meta-data support:

  1. Run-time performance sensitive situations.
  2. Compatibility with languages that don't support annotation or attributes.
  3. Any context where the interested code may not have access to the metadata.
  4. Support for generic constraints and generic variance (typically of collections).
like image 88
LBushkin Avatar answered Nov 02 '22 02:11

LBushkin


For a generic type you might want to use the same generic parameter in a marker interface. This is not achievable by an attribute:

interface MyInterface<T> {}

class MyClass<T, U> : MyInterface<U> {}

class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}

This kind of interface might be useful to associate a type with another one.

Another good use for a marker interface is when you want to create a kind of mixin:

interface MyMixin {}

static class MyMixinMethods {
  public static void Method(this MyMixin self) {}
}

class MyClass : MyMixin {
}

The acyclic visitor pattern also uses them. The term "degenerate interface" is sometimes used as well.

UPDATE:

I don't know if this one counts, but I've used them to mark classes for a post-compiler to work on.

like image 22
Jordão Avatar answered Nov 02 '22 01:11

Jordão


Microsoft didn't strictly follow the guidelines when they made .NET 1.0, because the guidelines evolved together with the framework, and some of the rules they didn't learn until it was too late to change the API.

IIRC, the examples you mention belong to BCL 1.0, so that would explain it.

This is explained in Framework Design Guidelines.


That said, the book also remarks that "[A]ttribute testing is a lot more costly than type checking" (in a sidebar by Rico Mariani).

It goes on to say that sometimes you need the marker interface for compile time checking, which isn't possible with an attribute. However, I find the example given in the book (p. 88) unconvincing, so I will not repeat it here.

like image 7
Mark Seemann Avatar answered Nov 02 '22 02:11

Mark Seemann