Compelling Reasons to Use Marker Interfaces Instead of Attributes

.Netasp.netAttributesInterfaceMarker Interfaces

.Net Problem Overview


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?

.Net Solutions


Solution 1 - .Net

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).

Solution 2 - .Net

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.

Solution 3 - .Net

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.

Solution 4 - .Net

From performance point of view:

Marker attributes will be slower than marker interfaces because of reflection. If you don't cache reflection then calling GetCustomAttributes all the time can be a performance bottleneck. I benchmarked this before and using marker interfaces win in terms of performance even when using cached reflection.

This only applies when you use it in the code that's frequently called.

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.371 (1709/FallCreatorsUpdate/Redstone3)
Intel Core i5-2400 CPU 3.10GHz (Sandy Bridge), 1 CPU, 4 logical and 4 physical cores
Frequency=3020482 Hz, Resolution=331.0730 ns, Timer=TSC
.NET Core SDK=2.1.300-rc1-008673
  [Host] : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT
  Core   : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT

Job=Core  Runtime=Core

                     Method |          Mean |      Error |     StdDev | Rank |
--------------------------- |--------------:|-----------:|-----------:|-----:|
                     CastIs |     0.0000 ns |  0.0000 ns |  0.0000 ns |    1 |
                     CastAs |     0.0039 ns |  0.0059 ns |  0.0052 ns |    2 |
            CustomAttribute | 2,466.7302 ns | 18.5357 ns | 17.3383 ns |    4 |
 CustomAttributeWithCaching |    25.2832 ns |  0.5055 ns |  0.4729 ns |    3 |

It's not significant difference though.

namespace BenchmarkStuff
{
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
    public class CustomAttribute : Attribute
    {

    }

    public interface ITest
    {

    }

    [Custom]
    public class Test : ITest
    {

    }

    [CoreJob]
    [RPlotExporter, RankColumn]
    public class CastVsCustomAttributes
    {
        private Test testObj;
        private Dictionary<Type, bool> hasCustomAttr;

        [GlobalSetup]
        public void Setup()
        {
            testObj = new Test();
            hasCustomAttr = new Dictionary<Type, bool>();
        }

        [Benchmark]
        public void CastIs()
        {
            if (testObj is ITest)
            {

            }
        }

        [Benchmark]
        public void CastAs()
        {
            var itest = testObj as ITest;
            if (itest != null)
            {

            }
        }

        [Benchmark]
        public void CustomAttribute()
        {
            var customAttribute = (CustomAttribute)testObj.GetType().GetCustomAttributes(typeof(CustomAttribute), false).SingleOrDefault();
            if (customAttribute != null)
            {

            }
        }

        [Benchmark]
        public void CustomAttributeWithCaching()
        {
            var type = testObj.GetType();
            bool hasAttr = false;
            if (!hasCustomAttr.TryGetValue(type, out hasAttr))
            {
                hasCustomAttr[type] = type.CustomAttributes.SingleOrDefault(attr => attr.AttributeType == typeof(CustomAttribute)) != null;
            }
            if (hasAttr)
            {

            }
        }
    }

    public static class Program
    {
        public static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<CastVsCustomAttributes>();
        }
    }
}

Solution 5 - .Net

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.

Solution 6 - .Net

From a coding point of view, I think I prefer the Marker Interface syntax because of the built in keywords as and is. Attribute marking requires a little bit more code.

[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;
    }
}

And some tests to use the code:

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);            
    }
} 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmmxView Question on Stackoverflow
Solution 1 - .NetLBushkinView Answer on Stackoverflow
Solution 2 - .NetJordãoView Answer on Stackoverflow
Solution 3 - .NetMark SeemannView Answer on Stackoverflow
Solution 4 - .NetKonradView Answer on Stackoverflow
Solution 5 - .NetherzmeisterView Answer on Stackoverflow
Solution 6 - .NetMikael EngverView Answer on Stackoverflow