views:

503

answers:

4

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?

+2  A: 

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.

Mark Seemann
Yes, they are seen breaking their own guidelines a lot of times, but there are usually reasons behind it. The question is, why they didn't follow this specific guideline? Are there any real advantages or simply a design mistake?
Mehrdad Afshari
I've always assumed that it was just a mistake. I don't have the book with me right now, but IIRC it specifically discusses examples where they used Marker Interfaces because 'they didn't know better'...
Mark Seemann
Now that I have had a chance to look it up in the book, I've added some more information to my answer.
Mark Seemann
@Mark: Yes, performance is the first point I mentioned (micro-optimization). I'm not convinced by it since ASMX uses it and it's possible to cache it. Compile-time checking is a convincing reason to use interfaces (as noted in the guideline and FxCop rule) but it doesn't apply to this specific case. Did they write anywhere that they say this was a mistake?
Mehrdad Afshari
Well, I didn't reread the entire book, but I looked up the pages pointed to by the index, and they didn't say anything about a mistake in that area. I probably mixed up the examples in my memory.
Mark Seemann
A: 

I'm strongly pro Marker Interfaces. I never liked Attributes. I see them as some kind of meta information for classes and members intended for example for debuggers to look at. Similar to Exceptions, they should not influence normal processing logic, in my most humble opinion.

herzmeister der welten
Can you elaborate why you think like that in your answer? I mean why do you think attributes and exceptions should not "influence normal processing logic"? (What do you mean by "normal processing logic"? exceptions certainly do influence flow of the program and are not just there for debuggers.)
Mehrdad Afshari
Okay, I'll try to elaborate a bit. First, .NET Attributes (or Java Annotations) are a relatively new invention, I think there isn't even a standard representation in UML for them. I know they are widely used for AOP and postprocessing now (PostSharp et al), but that only tries to make up for a certain lack in features or too much restrictiveness of the programming language.
herzmeister der welten
By "normal processing logic" I mean the regular "control flow" in an application or system, which is not "exception"al. Some people throw wild Exceptions when they want to return something that doesn't fit into the simple return datatype they chose, when they better just should have modeled things differently. It's a similar thing with Attributes. Take for example the [MaxLength(x)] Attributes for property meta data, or the [DefaultValue("foo")] one who is my special friend because he is not localizable.
herzmeister der welten
These things would belong into dedicated Type meta data classes and not into attributes. What if I want to initialize these things by a config file or from a database? I had to fiddle around with the TypeDescriptor and PropertyDescriptor stuff to make things work, and it felt very dirty. All in all, almost everything that can be expressed with Attributes can also be modeled differently, be it by the means of better support classes or a support for configuration. Also for post processing for example I would find a Marker Interface approach cleaner.
herzmeister der welten
Then again, anything that can be done with classes can be modeled differently. The point is they can make expressing some stuff significantly easier.
Mehrdad Afshari
+2  A: 

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).
LBushkin
+3  A: 

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 {
}

Also, I'll tend to use a marker interface anytime that the relationship between the interface and the marked class is permanent.

This is permanent:

interface IFood {}
class Pizza : IFood {}

While IRequiresSessionState is not, so I would prefer to use an attribute in this case.

When you say "marker interface", it seems that they will only serve to mark a class with some "attribute". But when you see these memberless interfaces as what they truly are, then you can find better uses to them, like in the acyclic visitor pattern (pdf). The term "degenerate interface" is also sometimes used to refer to them.

Jordão
+1, I like the mixin idea.
tster