What is the purpose of a marker interface?
Asked Answered
N

10

110

What is the purpose of a marker interface?

Neibart answered 21/6, 2009 at 2:38 Comment(0)
M
83

This is a bit of a tangent based on the response by "Mitch Wheat".

Generally, anytime I see people cite the framework design guidelines, I always like to mention that:

You should generally ignore the framework design guidelines most of the time.

This isn't because of any issue with the framework design guidelines. I think the .NET framework is a fantastic class library. A lot of that fantasticness flows from the framework design guidelines.

However, the design guidelines do not apply to most code written by most programmers. Their purpose is to enable the creation of a large framework that is used by millions of developers, not to make library writing more efficient.

A lot of the suggestions in it can guide you to do things that:

  1. May not be the most straightforward way of implementing something
  2. May result in extra code duplication
  3. May have extra runtime overhead

The .net framework is big, really big. It's so big that it would be absolutely unreasonable to assume that anyone has detailed knowledge about every aspect of it. In fact, it's much safer to assume that most programmers frequently encounter portions of the framework they have never used before.

In that case, the primary goals of an API designer are to:

  1. Keep things consistent with the rest of the framework
  2. Eliminate unneeded complexity in the API surface area

The framework design guidelines push developers to create code that accomplishes those goals.

That means doing things like avoiding layers of inheritance, even if it means duplicating code, or pushing all exception throwing code out to "entry points" rather than using shared helpers (so that stack traces make more sense in the debugger), and a lot of other similar things.

The primary reason that those guidelines suggest using attributes instead of marker interfaces is because removing the marker interfaces makes the inheritance structure of the class library much more approachable. A class diagram with 30 types and 6 layers of inheritance hierarchy is very daunting compared to one with 15 types and 2 layers of hierarchy.

If there really are millions of developers using your APIs, or your code base is really big (say over 100K LOC) then following those guidelines can help a lot.

If 5 million developers spend 15 mins learning an API rather than spending 60 mins learning it, the result is a net savings of 428 man years. That's a lot of time.

Most projects, however, don't involve millions of developers, or 100K+ LOC. In a typical project, with say 4 developers and around 50K loc, the set of assumptions are a lot different. The developers on the team will have a much better understanding of how the code works. That means that it makes a lot more sense to optimize for producing high quality code quickly, and for reducing the amount of bugs and the effort needed to make changes.

Spending 1 week developing code that is consistent with the .net framework, vs 8 hours writing code that is easy to change and has fewer bugs can result in:

  1. Late projects
  2. Lower bonuses
  3. Increased bug counts
  4. More time spent at the office, and less time on the beach drinking margaritas.

Without 4,999,999 other developers to absorb the costs it usually isn't worth it.

For example, testing for marker interfaces comes down to a single "is" expression, and results in less code that looking for attributes.

So my advice is:

  1. Follow the framework guidelines religiously if you are developing class libraries (or UI widgets) meant for wide spread consumption.
  2. Consider adopting some of them if you have over 100K LOC in your project
  3. Otherwise ignore them completely.
Mamelon answered 21/6, 2009 at 3:37 Comment(9)
I, personally, see any code that I write as a library I will need to use later. I don't really care of the consumption is widespread or not - following guidelines increases consistency, and reduces surprise when I need to look at my code and understand it years later...Hallo
I'm not saying guidelines are bad. I am saying they should be different, depending on the size of your code base, and the number of users you have. A lot of the design guidelines are based on things like maintaing binary comparability, which isn't as inmportant for "internal" libraries used by a handfull of projects as it is for something like the BCL. Other guidelines, like the ones related to usability, are almost always important. The moral is to not be overly religious about the guidelines, particularly on small projects.Mamelon
+1 - Didn't quite answer the OP's question - Purpose of MI - But very helpful nonetheless.Stupefy
@ScottWisniewski: I think you are missing serious points. The Framework guidelines just do not apply to large project, they are apply to medium and some small project. They become over-kill when you always try to apply them to Hello-World program. For example, limiting interfaces to 5 methods is always a good rule-of-thumb regardless of app-size. Another thing you miss, the small app today may become the large app of tomorrow. So, it better you build it with good principles that apply to large apps in mind so that when it comes time to scale up, you do not have to re-write a lot of code.Rambler
I don't quite see how following (most of) the design guidelines would result to a 8 hour project suddenly taking 1 week. ex: Naming a virtual protected template method DoSomethingCore instead of DoSomething is not that much additional work and you communicate clearly that it is a template method... IMNSHO, the people that write applications without considering the API (But.. I'm not a framework developer, I don't care about my API!) are exactly those people that write lots of duplicated (and also undocumented and usually unreadable) code, not the other way around.Glossy
+several for @ScottWisniewski. Systems designed for different goals require different designs. Some guidelines are generally applicable, some are specific to particular circumstances. Writing a framework is different from writing a library and again from writing a module within an application. All of those situations require strong adherence to guidelines; but some of those guidelines will be different.Fumigant
TL;DR... did you read the question? I'd rather read the warranty pamphlet for an appliance than this blather.Picaroon
@RickO'Shea Sorry you feel that way. Yes, I did read the OP. The first sentence says "this is a tangent". Anyways... the crux of my post is about how quality standards are relative and need to be based on cost benefit analysis. It was relevant to the discussion because of references to the framework design guidelines. If you think quality standards are not relative, I would ask you to consider cases such as NASA. If you followed their quality bar, it would bankrupt you. If they didn't people would die.Mamelon
"You should generally ignore the framework design guidelines most of the time." What's totally wrong. You should not ignore the framework design guidelines most of the time. But you can ignore it if ...here your reasons... Guidelines save me a lot of time, solution in it much more flexible and reusable. I hate then some coders recreate their own "bicycles" with no reason.Inclose
I
57

Marker Interfaces are used to mark the capability of a class as implementing a specific interface at run-time.

The Interface Design and .NET Type Design Guidelines - Interface Design discourage the use of marker interfaces in favour of using attributes in C#, but as @Jay Bazuzi points out, it is easier to check for marker interfaces than for attributes: o is I

So instead of this:

public interface IFooAssignable {} 

public class Foo : IFooAssignable 
{
    ...
}

The .NET guidelines recommended that you do this:

public class FooAssignableAttribute : Attribute 
{
    ...
}

[FooAssignable]
public class Foo 
{    
   ...
} 
Ilonailonka answered 21/6, 2009 at 2:43 Comment(7)
Also, we can fully use generics with marker interfaces, but not with attributes.Breckenridge
While I love attributes and how they look from a declarative standpoint, they are not first class citizens at runtime and require a significant amount of relatively low-level plumbing to work with.Penance
@Jordão - This was my thought exactly. As an example, if I want to abstract database access code (say Linq to Sql), having a common interface makes it A LOT easier. In fact, I don't think it would be possible to write that kind of abstraction with attributes since you can't cast to an attribute and can't use them in generics. I suppose you could use an empty base class from which the other classes all derive, but that feels more or less the same as having an empty interface. Plus, if you later realize you need shared functionality, the mechanism is already in place.Lorenelorens
@JesseC.Slicer I'd have agreed with you a long time ago. Generally, a simple extension method or two are all you need to work with Attributes these days.Lafountain
@Lafountain The me of twelve years ago and me now might have to have some words about design and approach 🤣Penance
@JesseC.Slicer roger that, sir. Same 🤣 here!Lafountain
But what words would those be, @JesseC.Slicer? I'm so intrigued!Thorlay
I
27

Since every other answer has stated "they should be avoided", it would be useful to have an explanation of why.

Firstly, why marker interfaces are used: They exist to allow the code that's using the object that implements it to check whether they implement said interface and treat the object differently if it does.

The problem with this approach is that it breaks encapsulation. The object itself now has indirect control over how it will be used externally. Moreover, it has knowledge of the system it's going to be used in. By applying the marker interface, the class definition is suggesting it expects to be used somewhere that checks for the existence of the marker. It has implicit knowledge of the environment it's used in and is trying to define how it should be being used. This goes against the idea of encapsulation because it has knowledge of the implementation of a part of the system that exists entirely outside its own scope.

At a practical level this reduces portability and reusability. If the class is re-used in a different application, the interface needs to be copied across too, and it may not have any meaning in the new environment, making it entirely redundant.

As such, the "marker" is metadata about the class. This metadata is not used by the class itself and is only meaningful to (some!) external client code so that it can treat the object in a certain manner. Because it only has meaning to the client code, the metadata should be in the client code, not the class API.

The difference between a "marker interface" and a normal interface is that an interface with methods tells the outside world how it can be used whereas an empty interface implies it's telling the outside world how it should be used.

Implosive answered 2/10, 2012 at 10:44 Comment(5)
The primary purpose of any interface is to distinguish between classes that promise to abide by the contract associated with that interface, and those which don't. While an interface is also responsible for supplying the calling signatures of any members necessary to fulfill the contract, it is the contract, rather than the members, which determines whether a particular interface should be implemented by a particular class. If the contract for IConstructableFromString<T> specifies that a class T may only implement IConstructableFromString<T> if it has a static member...Geist
...public static T ProduceFromString(String params);, a companion class to the interface can offer a method public static T ProduceFromString<T>(String params) where T:IConstructableFromString<T>; if client code had a method like T[] MakeManyThings<T>() where T:IConstructableFromString<T>, one could define new types that could work with the client code without having to modify the client code to deal with them. If the metadata were in the client code, it wouldn't be possible to create new types for use by the existing client.Geist
But the contract between T and the class that uses it is IConstructableFromString<T> where you have a method in the interface that describes some behaviour so it's not a marker interface.Implosive
The static method that the class is required to have isn't part of the interface. Static members in interfaces are implemented by the interfaces themselves; there's no way for an interface to refer to a static member in an implementing class.Geist
It's possible for a method to determine, using Reflection, whether a generic type happens to have a particular static method, and execute that method if it exists, but the actual process of searching for and executing the static method ProduceFromString in the example above would not involve the interface in any way, except that the interface would be used as a marker to indicate what classes should be expected to implement the necessary function.Geist
M
10

Marker interfaces may sometimes be a necessary evil when a language does not support discriminated union types.

Suppose you want to define a method who expects an argument whose type must be exactly one of A, B, or C. In many functional-first languages (like F#), such a type can be cleanly defined as:

type Arg = 
    | AArg of A 
    | BArg of B 
    | CArg of C

However, in OO-first languages such as C#, this is not possible. The only way to achieve something similar here is to define interface IArg and "mark" A, B and C with it.

Of course, you could avoid using the marker interface by simply accepting type "object" as argument, but then you would lose expressiveness and some degree of type safety.

Discriminated union types are extremely useful and have existed in functional languages for at least 30 years. Strangely, to this day, all mainstream OO languages have ignored this feature -- although it has actually nothing to do with functional programming per se, but belongs to the type system.

Martainn answered 10/7, 2013 at 10:8 Comment(1)
It's worth noting that because a Foo<T> will have a separate set of static fields for every type T, it's not hard to have a generic class contain static fields containing delegates to process a T, and pre-populate those fields with functions to handle every type the class is supposed to work with. Using a generic interface constraint upon type T would check at compiler time that the supplied type at least claimed to be valid, even though it wouldn't be able to ensure that it actually was.Geist
E
8

A marker interface is just an interface that is empty. A class would implement this interface as metadata to be used for some reason. In C# you would more commonly use attributes to mark up a class for the same reasons you'd use a marker interface in other languages.

Exerciser answered 21/6, 2009 at 2:48 Comment(0)
C
6

These two extension methods will solve most of the issues Scott asserts favor marker interfaces over attributes:

public static bool HasAttribute<T>(this ICustomAttributeProvider self)
    where T : Attribute
{
    return self.GetCustomAttributes(true).Any(o => o is T);
}

public static bool HasAttribute<T>(this object self)
    where T : Attribute
{
    return self != null && self.GetType().HasAttribute<T>()
}

Now you have:

if (o.HasAttribute<FooAssignableAttribute>())
{
    //...
}

versus:

if (o is IFooAssignable)
{
    //...
}

I fail to see how building an API will take 5 times as long with the first pattern compared to the second, as Scott claims.

Chagrin answered 14/6, 2016 at 5:54 Comment(1)
Still no generics.Andradite
G
4

A marker interface allows a class to be tagged in a way that will be applied to all descendant classes. A "pure" marker interface wouldn't define or inherit anything; a more useful type of marker interfaces may be one which "inherits" another interface but defines no new members. For example, if there is an interface "IReadableFoo", one might also define an interface "IImmutableFoo", which would behave like a "Foo" but would promise anyone who uses it that nothing would change its value. A routine which accepts an IImmutableFoo would be able to use it as it would an IReadableFoo, but the routine would only accept classes that were declared as implementing IImmutableFoo.

I can't think of a whole lot of uses for "pure" marker interfaces. The only one I can think of would be if EqualityComparer(of T).Default would return Object.Equals for any type which implemented IDoNotUseEqualityComparer, even if the type also implemented IEqualityComparer. This would allow one to have an unsealed immutable type without violating the Liskov Substitution Principle: if the type seals all methods related to equality-testing, a derived type could add additional fields and have them be mutable, but the mutation of such fields wouldn't be visible using any base-type methods. It might not be horrible to have an unsealed immutable class and either avoid any use of EqualityComparer.Default or trust derived classes not to implement IEqualityComparer, but a derived class which did implement IEqualityComparer could appear as a mutable class even when viewed as a base-class object.

Geist answered 24/1, 2011 at 16:24 Comment(0)
R
1

Markers are empty interfaces. A marker is either there or it isn't.

class Foo : IConfidential

Here we mark Foo as being confidential. No actual additional properties or attributes required.

Reinforcement answered 10/2, 2017 at 6:47 Comment(0)
A
1

The marker interface is really just a procedural programming in an OO language. An interface defines a contract between implementers and consumers, except a marker interface, because a marker interface defines nothing but itself. So, right out of the gate, the marker interface fails at the basic purpose of being an interface.

Accouter answered 9/7, 2018 at 12:0 Comment(0)
F
0

Marker interface is a total blank interface that has no body/data-members/implementation.
A class implements marker interface when required, it is just to "mark"; means it tells the JVM that the particular class is for the purpose of cloning so allow it to clone. This particular class is to Serialize its objects so please allow its objects to get serialized.

Forerunner answered 8/3, 2018 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.