How to use default interface methods in C#
By FoxLearn 1/3/2025 7:54:35 AM 89
Prior to C# 8.0, interfaces could only declare methods, and any change (like adding a new method) required updating all implementing classes. With default interface methods, you can now provide method implementations directly in the interface, making it easier to evolve interfaces without breaking existing code.
Additionally, C# 8.0 expands the capabilities of interfaces by allowing members to be private, protected, static, virtual, and abstract. However, virtual members can only be overridden by derived interfaces, not by classes implementing the interface.
Why use default interface methods?
Default interface methods in C# are methods defined in an interface with concrete implementations. If a class that implements the interface does not provide its own implementation for the method, the default implementation from the interface is used instead. This feature allows developers to add new methods to an interface in future versions without breaking existing implementations, ensuring backward compatibility.
Imagine you're building a payment processing system, and you have an interface IPaymentProcessor
for processing different types of payments.
public interface IPaymentProcessor { void ProcessPayment(decimal amount); } public class CreditCardProcessor : IPaymentProcessor { public void ProcessPayment(decimal amount) { // Process payment through credit card Console.WriteLine($"Processing credit card payment of {amount:C}"); } } public class PayPalProcessor : IPaymentProcessor { public void ProcessPayment(decimal amount) { // Process payment through PayPal Console.WriteLine($"Processing PayPal payment of {amount:C}"); } }
At this point, both CreditCardProcessor
and PayPalProcessor
are implementing the IPaymentProcessor
interface. Now, suppose you want to add a new method to the IPaymentProcessor
interface that allows you to process a payment with additional details like a currency type.
If you add a new method ProcessPayment(decimal amount, string currency)
directly to the interface like this:
public interface IPaymentProcessor { void ProcessPayment(decimal amount); void ProcessPayment(decimal amount, string currency); }
Now, both CreditCardProcessor
and PayPalProcessor
must implement the new method ProcessPayment(decimal amount, string currency)
. If you forget to implement this new method in any of the classes, the compiler will raise an error. This could be problematic, especially if the IPaymentProcessor
interface is used in various parts of your system or even across different teams.
Solution with Default Interface Methods
With default interface methods, you can provide a default implementation for the new method right inside the interface itself. This way, you avoid breaking existing implementations because classes that don’t implement the new method will use the default behavior.
Here's how you would modify the IPaymentProcessor
interface to include the new method with a default implementation:
public interface IPaymentProcessor { void ProcessPayment(decimal amount); // Default implementation for the new method void ProcessPayment(decimal amount, string currency) { // Default behavior: Process payment with the specified currency Console.WriteLine($"Processing payment of {amount:C} in {currency}"); } }
Now, the existing CreditCardProcessor
and PayPalProcessor
classes don’t need to implement the new ProcessPayment(decimal amount, string currency)
method unless they want to customize the behavior. If they don’t implement it, the default implementation will be used.
public class CreditCardProcessor : IPaymentProcessor { public void ProcessPayment(decimal amount) { // Process payment through credit card Console.WriteLine($"Processing credit card payment of {amount:C}"); } } public class PayPalProcessor : IPaymentProcessor { public void ProcessPayment(decimal amount) { // Process payment through PayPal Console.WriteLine($"Processing PayPal payment of {amount:C}"); } }
In this case, both CreditCardProcessor
and PayPalProcessor
will still use the default behavior for the ProcessPayment(decimal amount, string currency)
method. The default implementation simply prints out the payment amount along with the currency.
Customizing the New Method
If a class needs custom behavior for the new method, it can override the default implementation:
public class CreditCardProcessor : IPaymentProcessor { public void ProcessPayment(decimal amount) { // Process payment through credit card Console.WriteLine($"Processing credit card payment of {amount:C}"); } // Override the default method to include specific behavior for currency public void ProcessPayment(decimal amount, string currency) { // Custom behavior for credit card payments in different currencies Console.WriteLine($"Processing credit card payment of {amount:C} in {currency}"); } }
This CreditCardProcessor
class now provides its own version of ProcessPayment(decimal amount, string currency)
, while the PayPalProcessor
can still rely on the default implementation.
By using default interface methods, you can evolve the IPaymentProcessor
interface to support new functionality (like accepting a currency) without forcing every implementing class to be updated immediately.
Default Interface Methods Are Not Inherited
In C# 8.0, default interface methods are not inherited by the classes that implement the interface. This means that while an interface can provide a default implementation for a method, the class implementing the interface doesn't automatically "inherit" that method as part of its own members.
For example, let's consider the ILogger
interface with the newly added Log
method that includes a LogLevel
parameter:
public interface ILogger { void Log(string message); // Default implementation for the new method void Log(string message, LogLevel logLevel) { // Default behavior: Log the message with the provided log level Console.WriteLine($"[{logLevel}] {message}"); } }
Now, let’s create an instance of the FileLogger
class and try calling the new Log
method:
FileLogger fileLogger = new FileLogger(); fileLogger.Log("This is a test message.", LogLevel.Debug);
Although the ILogger
interface provides a default implementation for the Log
method with two parameters, the FileLogger
class doesn't automatically inherit this method. The class doesn't "know" about the default method unless it explicitly implements it.
Abstract classes vs. interfaces in C# 8.0
In C# 8.0, abstract classes and interfaces share some similarities but are not the same. Key differences include that a class can still only inherit from one abstract class, whereas multiple interfaces can be implemented. Additionally, interfaces cannot have instance members, unlike abstract classes.
With default interface methods, developers can use a technique called traits programming, which allows for reusing methods across unrelated types. This is particularly useful when releasing new versions of a library. By adding new members to interfaces with default implementations, you can extend the interface's functionality without requiring changes to the existing codebase, ensuring backward compatibility.
- How to fix 'Failure sending mail' in C#
- How to Parse a Comma-Separated String from App.config in C#
- How to convert a dictionary to a list in C#
- How to retrieve the Executable Path in C#
- How to validate an IP address in C#
- How to retrieve the Downloads Directory Path in C#
- C# Tutorial
- Dictionary with multiple values per key in C#