Take advantage of default interface methods in C# 8.0 to add new methods to an interface without breaking existing implementations. Credit: Dean Mitchell / Getty Images One of the new and interesting features in C# 8.0 is the support for default interface methods (also known as virtual extension methods). This article presents a discussion of default interface methods and how we can work with them in C# 8.0. Until C# 8.0, an interface in C# could not contain method definitions. You could only include method declarations in an interface, and the members of an interface were public and abstract by default. Further, until C# 8.0, an interface could not contain fields, nor could it have private, protected, or internal members. If you introduced a new member (i.e., a method declaration) in an interface, all of your classes that implement the interface would have to be updated. With C# 8.0, you can now have default implementations of methods in an interface. Interface members can be private, protected, and static as well. Protected members of an interface cannot be accessed in the class that extends the interface. Rather, they can be accessed only in the derived interface. Interface members can also be virtual and abstract. However, virtual members of an interface can be overridden by the derived interface but not by a class that extends the interface. Note that you still can’t include an instance member in an interface. Why use default interface methods? Default interface methods are methods in an interface that contain concrete implementations. If the class that extends the interface doesn’t implement the method, the default implementation of the method will be used from the interface. This is a useful feature as it helps developers add methods to future versions of an interface without breaking the existing functionality, i.e., the existing implementations of that interface. Consider the following interface ILogger. public interface ILogger { public void Log(string message); } The following two classes extend the ILogger interface and implement the Log() method. public class FileLogger : ILogger { public void Log(string message) { //Some code } } public class DbLogger : ILogger { public void Log(string message) { //Some code } } So far so good. Now, suppose you want to introduce a new method in the ILogger interface that would accept two parameters, the text message to be logged and the log level. The following code snippet shows the log level enumeration. public enum LogLevel { Info, Debug, Warning, Error } Here’s how the ILogger interface looks now. public interface ILogger { public void Log(string message); public void Log(string message, LogLevel logLevel); } Now, here’s the problem: You’ll be forced to implement this new method in all of the classes that extend the ILogger interface. If you don’t, the compiler will flag an error stating that the new method hasn’t been implemented. Your interface might have been used in several other libraries and even across teams. So, implementing this change will be a painful and perhaps daunting task. Default interface methods example This is where default interface methods come to the rescue. You can provide a default implementation for your new Log method as shown in the code snippet given below. public interface ILogger { public void Log(string message); public void Log(string message, LogLevel logLevel) { Console.WriteLine("Log method of ILogger called."); Console.WriteLine("Log Level: "+ logLevel.ToString()); Console.WriteLine(message); } } Classes that extend the ILogger interface are no longer required to implement the new Log method. Hence, the following code is valid—the compiler will not throw an error. public class FileLogger : ILogger { public void Log(string message) { //Some code } } public class DbLogger : ILogger { public void Log(string message) { //Some code } } Default interface methods are not inherited Now create an instance of the FileLogger class as shown in the code snippet below, and call the Log method we added newly to the ILogger interface. FileLogger fileLogger = new FileLogger(); fileLogger.Log("This is a test message.", LogLevel.Debug); IDG Default interface methods are not inherited. So, this proves that default interface methods are not inherited by the extending class, i.e., the class extending the interface has no knowledge of the default interface methods. Default interface methods and the diamond problem And now the million dollar question is, how do default interface methods avoid “the diamond problem,” i.e., how does it resolve issues of multiple inheritance using interfaces? Let’s examine this with an example. Consider the following code listing. public interface A { public void Display(); } public interface B : A { public void Display() { Console.WriteLine("Interface B."); } } public interface C : A { public void Display() { Console.WriteLine("Interface C."); } } public class MyClass : B, C { } When you compile the above program, you will be presented with a compilation error stating that the class MyClass doesn’t implement the interface member A.Display(), and rightly so. Because the Display method lacks a default implementation in interface A, we must provide an implementation in the class MyClass to satisfy the compiler. Let’s do that. Here is the updated code for your reference. public interface A { public void Display(); } public interface B : A { public void Display() { Console.WriteLine("Interface B."); } } public interface C : A { public void Display() { Console.WriteLine("Interface C."); } } public class MyClass : B, C { public void Display() { Console.WriteLine("MyClass."); } } Now create an instance of the class MyClass and call the Display method as shown in the code snippet given below. static void Main(string[] args) { A obj = new MyClass(); obj.Display(); Console.Read(); } Which Display method will be called this time? To avoid ambiguity, the most specific override rule will be applied. Hence, the Display method of the class MyClass will be called. Abstract classes vs. interfaces in C# 8.0 Are abstract classes and interfaces the same in C# 8.0? Not really. Although abstract classes and interfaces now seem similar in more ways than one, there are subtle differences between the two. You still can’t extend a class from more than one class. And unlike an abstract class, an interface cannot have instance members. Moreover, you still can implement several interfaces. Default interface methods allow developers to leverage the traits programming technique, a technique that enables you to reuse methods pertaining to unrelated types. Let’s say you have built a library that will be consumed by many developers, and you would like to release a new version of the library. If your interfaces have new members added, you can define their implementations so that you can continue to leverage the types in the library without the need of altering the codebase. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos