If someone asks me “Can we have access modifiers in C# interface”, my answer would be obviously “Hell NO “.
From the beginning, as far as I know, it was impossible to implement any type (with access modifier private, protected, or internal) of members in the interface, nor it contains any field.
By default, the obvious reason was that interfaces are public and abstract, and inheriting classes must provide the implementation. Thus we were allowed only to implement methods declaration.
Finally, moving forwared the interview question becomes pretty much irrelevent for the next generation to come.
With C# 8, Things are changes, now we can have a default implementation of methods in an interface as well as interface members can be private, protected, internal or static.
Let’s talk about the “Default implementation”, and what benefit it brings with it.
Earlier we used to have interface implemented in a number of classes. If we need a new member we used to:
- Eiter we update all the classes to support the new member.
- Or we extend the interface with new method/member and inherit with exiting interface
interface ICar { void GetSpeed(); void GetMilage(); } interface IAutonomousCar: ICar { void SendCommand(); }
I guess we all know where we ended up with such implementation. The list of extended interfaces grows till it becomes unmanageable.
C# 8 answers to this by introducing “Default implementation in interfaces” which adds support for virtual extension methods – methods in interface with a concrete implementation.
Concrete methods in interfaces
We can now add members to interface and provide an implementation for those members. This feature enables an API author to add methods to an interface in a future version without breaking source or library with the existing implementation. Very much like Java’s “Default Methods“.
By the way, this feature requires support in the CLI/CLR and programmes that take advantage of this feature cannot run on earlier versions of the platform.
Following class implements this interface need not implement its concrete method:
interface ICar { void GetSpeed(); void GetMilage(); void SendCommand() { Console.WriteLine("Command Send via Interface"); } } class ToyotaGarage : ICar { public void GetSpeed() { Console.WriteLine("200 KMPH"); } public void GetMilage() { Console.WriteLine("10 KM Per Liter"); } }
We did not implement SendCommand
method into the class
ToyotaGarage
. However, the default implementation will be available from an instance of the interface:
class Program { static void Main(string[] args) { ICar tg = new ToyotaGarage(); tg.SendComand(); // prints "Command Send via Interface" } }
C# 8 now allow us to override a concrete method of the interface in the inherited class. :
class TeslaGarage : ICar { public void GetSpeed() { Console.WriteLine("350 KMPH"); } public void GetMilage() { Console.WriteLine("50 KM Per Charge") } override void SendCommand() { Console.WriteLine("Command Send via TeslaGarage"); } }
In this example, TeslaGarage
simply override the concrete method SendCommandof
the interface.
Overrides in interfaces
The override statement allows us to provide the most specific implementation of a virtual member in the interface, where the member will not be found by the compiler or runtime. It also allows turning an abstract member from a super-interface into a default member in a derived interface.
In simple a derived interface can provide a more appropriate default implementation by explicitly implementing the base member:
Override declaration in interfaces may not be declared sealed.
interface IA { void M() { WriteLine("IA.M"); } } interface IB : IA { override void IA.M() { WriteLine("IB.M"); } // explicitly named } interface IC : IA { override void M() { WriteLine("IC.M"); } // implicitly named }
We explicitly implement the base member by including IA.
in the name. Without IA.
, the compiler would warn us to either make it explicit or use the new
keyword if we want to hide it.
Modifiers in interfaces
C# 8 now support modifiers on its members such as : private
, protected
, internal
, public
, virtual
, abstract
, sealed
, static
, extern
, and partial
.
Internal members only can be accessed through the derived interface
An interface member with a concrete method is by default a virtual
member unless explicitly sealed
or private
modified are used. virtual
members cant be overridden by class, the only derived interfaces can override it:
public interface ICar { public virtual void SendCommand() { Console.WriteLine("Command Sent via Interface"); } } public interface IAnotherCar :ICar { void ICar.SendCommand() { Console.WriteLine("Command Sent via another Interface"); } } class MorrisGarage: ICar, IAnotherCar { } class Program { static void Main() { ICar mg= new MorrisGarage(); mg.SendCommand(); //Calls the virtual implementation. IAnotherCar mgOverridden = new MorrisGarage(); mgOverridden.SendCommand(); //Calls the overridden implementation. } }
Virtual Modifier vs sealed Modifier (Source)
It is allowed that modifiers to be stated on interface members unless there is a reason to disallow some of them. This brings an interesting question around virtual modifier. Should it be required on members with default implementation?
We could say that:
- if there is no implementation and neither
virtual
norsealed
are specified, we assume the member isabstract
. - if there is an implementation and neither
abstract
norsealed
are specified, we assume the member isvirtual
. sealed
modifier is required to make a method neithervirtual
norabstract
.
Alternatively, we could say that virtual
modifier is required for a virtual
member. I.e, if there is a member with implementation not explicitly marked with virtual
modifier, it is neither virtual
, nor abstract
. This approach might provide a better experience when a method is moved from a class to an interface:
- an
abstract
method staysabstract
. - a
virtual
method staysvirtual
. - a method without any modifier stays neither
virtual
, norabstract
. sealed
modifier cannot be applied to a method that is not an override.
What do you think? comment below
Similar can be applied on abstract
modifier, although
abstract
is a default on all members (without body or concrete method) of the interface, that modifier may be given explicitly. Although every class need to implement the interface methods.
protected
members are not accessible by derived class but via the derived interface. If a class requires to implement protected
member, this has to be done through interface explicitly:
public interface ICar { public void SendCommand() { Console.WriteLine("Command Sent via Interface"); } protected void SendCriticalCommand() { Console.WriteLine("Critical Command Sent via Interface"); } } public interface IAnotherCar : ICar { public void Send(bool bCritical) { if (bCritical) this.SendCriticalCommand(); else Console.WriteLine("Command Sent via Morris Garage Class"); } }
Last but not least, the interface can declare a
static
member, including nested types, methods, properties event and static constructors.
CLR support API
In order for compilers to detect when they are compiling for a runtime that supports this feature, libraries for such runtimes are modified to advertise that fact through the API discussed in https://github.com/dotnet/corefx/issues/17116. As it add:
namespace System.Runtime.CompilerServices { public static class RuntimeFeature { // Presence of the field indicates runtime support public const string DefaultInterfaceImplementation = nameof(DefaultInterfaceImplementation); } }
Abstract classes vs. interfaces in C# 8.0
Now the question arises if abstract classes and interfaces are the same in new C#? Although they are very similar in more ways than one, there are subtle differences between the two. We cannot extend a class and unlike an abstract class, the interface can not have instance members and you still can implement several interfaces.
Conclusion
The default interface method is very useful, which allows the developer to release new changes in any interface as it progresses without breaking anything.
As usual, this received mix feedback but I am thrilled and believe it is moving forward. Java and Scala already have this feature which enables C# to interoperate with APIs targeting Android (Java) and iOS (Swift), which support similar features.
This feature is taking C# into the direction from Oops to functional programming. As it turns out, adding default interface implementations provides the elements of the “traits” language features. Traits have proven to be a powerful programming technique.