.NET Type Design Guidelines—Interface Design
| Visual C# Tutorials |
| .NET Framework Tutorials |
| © 2006 Microsoft Corp. |
Interface Design
Although most APIs are best modeled using classes and structs, there are cases in which interfaces are more appropriate or are the only option.
The CLR does not support multiple inheritance (i.e., CLR classes cannot inherit from more than one base class), but it does allow types to implement one or more interfaces in addition to inheriting from a base class. Therefore interfaces are often used to achieve the effect of multiple inheritance. For example, IDisposable is an interface that allows types to support disposability independent of any other inheritance hierarchy in which they want to participate.
public class Component : MarshalByRefObject, IDisposable, IComponent { ... }
The other situation in which defining an interface is appropriate is in creating a common interface that can be supported by several types including some value types. Value types cannot inherit from types other than System.ValueType, but they can implement interfaces, so using an interface is the only option to provide a common base type.
public struct Boolean : IComparable { ... } public class String: IComparable { ... }
- DO define an interface if you need some common API to be supported by a set of types that includes value types.
- CONSIDER defining an interface if you need to support its functionality on types that already inherit from some other type.
- AVOID using marker interfaces (interfaces with no members).
- If you need to mark a class as having a specific characteristic (marker), in general, use a custom attribute rather than an interface.
// Avoid public interface IImmutable {} // empty interface public class Key: IImmutable { ... } //Do [Immutable] public class Key { ... }
Methods can be implemented to reject parameters that are not marked with a specific attribute as follows:
public void Add(Key key, object value){ if(!key.GetType().IsDefined(typeof(ImmutableAttribute), false)){ throw new ArgumentException("The parameter must be immutable","key"); } }
| RICO MARIANI |
| Of course any kind of marking like this has a cost. Attribute testing is a lot more costly than type checking. You might find that it's necessary to use the marker interface approach for performance reasons—measure and see. My own experience is that true markers (with no members) don't come up very often. Most of the time, you need a no- kidding-around interface with actual functionality to do the job, in which case there is no choice to make. |
The problem with this approach is that the check for the custom attribute can occur only at runtime. Sometimes, it is very important that the check for the marker be done at compile-time. For example, a method that can serialize objects of any type might be more concerned with verifying the presence of the marker than with type verification at compile-time. Using marker interfaces might be acceptable in such situations. The following example illustrates this design approach:
public interface ITextSerializable {} // empty interface public void Serialize(ITextSerializable item){ // use reflection to serialize all public properties ... }
- DO provide at least one type that is an implementation of an interface.
- This helps to validate the design of the interface. For example,
System.Collections.ArrayListis an implementation of theSystem.Collections.IListinterface.
- DO provide at least one API consuming each interface you define (a method taking the interface as a parameter or a property typed as the interface).
- This helps to validate the interface design. For example,
List<T>.SortconsumesIComparer<T>interface.
- DO NOT add members to an interface that has previously shipped.
- Doing so would break implementations of the interface. You should create a new interface to avoid versioning problems.
Except for the situations described in these guidelines, you should, in general, choose classes rather than interfaces in designing managed code reusable libraries.
|

