An interface member is a component of an interface, which defines a contract that a class, struct, or record must implement.
A class that implements the interface is required to provide a concrete implementation for all of its members, adhering to the specified contract. Interface members define the what—the public behavior or capabilities of a type—without specifying the how—the implementation details.
Core types of interface members
Methods
These are function declarations without a body, defining a specific action that implementing types must perform.
-
**Example:**csharp
interface ILogger { void LogMessage(string message); }Use code with caution.
Properties
These define accessors (get and set) for data members but do not declare instance fields.
-
**Example:**csharp
interface IHasId { int Id { get; set; } }Use code with caution.
Indexers
Similar to properties, indexers allow a class to be indexed like an array. The interface defines the signature of the indexer but no implementation.
-
**Example:**csharp
interface IStringList { string this[int index] { get; set; } }Use code with caution.
Events
Events define a notification mechanism that an implementing class must provide. The interface specifies the event signature.
-
**Example:**csharp
interface INotify { event Action OnActionCompleted; }Use code with caution.
Modern enhancements to interfaces
C# versions starting from 8.0 have significantly expanded the capabilities of interfaces beyond just abstract members.
Default interface methods
Introduced in C# 8.0, default interface methods allow an interface to provide a default implementation for a member. This is useful for evolving an interface without breaking existing code. Implementing classes can either use the default implementation or override it.
Use case: Expanding an interfaceImagine you have an existing ILogger interface with many implementations. If you need to add a new LogMessageWithTimestamp() method, adding it as a default method prevents a breaking change.
-
**Example:**csharp
interface ILogger { void LogMessage(string message); // Default method introduced in C# 8.0 void LogMessageWithTimestamp(string message) { LogMessage($"[{DateTime.Now}] {message}"); } }Use code with caution.
Static interface members
Introduced in C# 8.0 (and expanded in C# 11), interfaces can contain static methods, properties, and constructors.
- Static methods are associated with the interface type itself, not an instance. They can be used to provide utility functions related to the interface.
- Static constructors run once to initialize static data.
- Static abstract members (C# 11+) are a powerful feature that declares that a static member must be provided by an implementing type. This enables generic algorithms to operate on types that have static members, such as mathematical operators.
Use case: Generic mathStatic abstract members in C# 11 allow for generic math algorithms. For example, you can write a generic Add<T>(T left, T right) method that works with any type T that implements an interface like IAdditionOperators<T, T, T>.
-
**Example:**csharp
public interface IAdditionOperators<TSelf, TOther, TResult> where TSelf : IAdditionOperators<TSelf, TOther, TResult> { static abstract TResult operator +(TSelf left, TOther right); }Use code with caution.
Other members
Interfaces can also contain other members, including:
- Constants: Public static and final variables that are part of the interface's contract (though this is more common in Java).
- Nested types: Interfaces can contain nested enums, structs, classes, or even other interfaces.
How implementing classes use interface members
Implicit implementation
With implicit implementation, a public, non-static member of the implementing class matches the name and signature of the interface member. This is the default and most common approach.
-
**Example:**csharp
class MyLogger : ILogger { public void LogMessage(string message) { Console.WriteLine(message); } }Use code with caution.
Explicit implementation
Explicit implementation is used to avoid naming conflicts when a class implements multiple interfaces with members of the same name. It is also used to hide interface-specific members from the public API of the implementing class.
-
**Example:**csharp
interface IReadable { string Read(); } interface IWriteable { void Write(string text); } class FileHandler : IReadable, IWriteable { // Explicit implementation for IReadable.Read() string IReadable.Read() { return "Reading from file..."; } // Explicit implementation for IWriteable.Write() void IWriteable.Write(string text) { Console.WriteLine($"Writing to file: {text}"); } }Use code with caution.
Interface members vs. class members
| Feature | Interface member | Class member |
|---|---|---|
| Purpose | Defines a contract for behavior. | Defines both the contract (public members) and the implementation (all members). |
| Implementation | By default, declarations only, though C# 8.0+ supports default implementations. | Can be abstract (without body) or concrete (with body). |
| Inheritance | A class can implement multiple interfaces. | A class can only inherit from a single base class. |
| State | Cannot hold instance fields (state). | Can define instance fields to store state. |
| Constructors | Cannot have instance constructors or destructors. | Can have constructors to initialize state. |
Key takeaways
- Interface members define a blueprint of behavior that classes and structs must follow.
- They enforce a contract, which promotes code flexibility, reusability, and loose coupling between components.
- Modern language features, such as default interface methods and static abstract members, have added greater power and utility to interfaces, blurring the lines with abstract classes while maintaining their core purpose.
- Understanding the different types of interface members and their implementation methods (implicit vs. explicit) is fundamental to object-oriented and type-safe programming.