C# 8.0 Features - Part 1 - Default Interface Members

C# 8.0 Features - Part 1 - Default Interface Members
Part 0 - Nullabe Reference Types
Part 1 - Default Interface Members
Part 2 - Pattern Matching Enhancements
Part 3 - All the Small Things

   The next major C# 8.0 feature I would like to comment on is the Default Interface Members. This feature is straight-forward. It simply allows you to define implementations for the methods, indexers and properties in the interface. Lets look at this example I made up that makes no actual sense.

interface IUser
{
   public string FirstName { get; }
   public string LastName { get; }

   public string ToHtmlString();
}

class User : IUser
{
   public User(string firstName, string lastName)
   {
       FirstName = firstName;
       LastName = lastName;
   }

   public string FirstName { get; }
   public string LastName { get; }

   public string ToHtmlString()
   {
       return $"{FirstName} <strong>{LastName}<strong>";
   }
}

   So we have an IUser and some method for displaying it as HTML. If you are careful you might have noticed that this was not a valid syntax in C# 7 and prior because public modifier was not allowed in interfaces. Well, now it is allowed (although not required here) and we will see why later. Other than that there is nothing new here.

   The main reason for the new feature is to be able to evolve interfaces without breaking clients. What if IUser was in a library and we wanted to add another method say ToMarkdownString(). In most cases we could have used an extension method but there are some cases where that does not work and preferably, we would have liked to make this part of the interface and allow users to implement it if they need to. So we add this method to the interface

public string ToMarkdownString()
{
   return $"{FirstName} **{LastName}**";
}

Now everyone who implements the interface gets this method for free. For example

IUser user = new User("Stilgar", "Fifrawi");
Console.WriteLine(user.ToMarkdownString());

   Note that the method can still be declared in the class in which case the declaration in the class will be used instead of the default one. Also note that this method is treated as an explicit implementation which means that if the variable is declared of type User and not IUser the code will not compile. To be honest I find this a bit strange but probably there is some reason for this that I am not seeing. While it is possible to call the method inside the class by casting this to the interface type I did not find a way to simply import the method into the class with the same name.

   You can also add static state and static methods to the interface. For example

private static string openingTag = "**";
private static string closingTag = "**";

public static void SetTags(string opening, string closing)
{
   openingTag = opening;
   closingTag = closing;
}

public string ToMarkdownString()
{
   return $"{FirstName} {openingTag}{LastName}{closingTag}"
}

You can call use this method like that:

IUser user = new User("Stilgar", "Fifrawi");
IUser.SetTags("*", "*");
Console.WriteLine(user.ToMarkdownString());

   You can have private and protected helper methods and you can have protected members that the class needs to implement which the default implementation can use. This is why access modifiers are now allowed.

   Unlike abstract classes interfaces still cannot contain (non-static) state. In computer science terms this makes C# interfaces traits (as opposed to actual interfaces which are simply contacts and mixins which can contain state).

   So what is the verdict? When I first heard about this feature, I thought it should not be added to the language. Sure, traits do have usages and it was nice to be able to evolve the interfaces more easily, but the feature adds a lot of complexity to the language and some inconsistency (default modifier for interface members is public but for classes it is private). In addition, extension methods cover quite a bit of what default implementations do. However, there was one argument that tips the scales in favor of this feature – Java/Kotlin and Swift have it. I am not saying that every feature from these languages must be copied but in this case it is not about the feature. It is about the fact that default methods start to show up in the APIs for iOS and Android which means that Xamarin needs to find a way to emulate it. If we agree that Xamarin is important for the future of .NET it seems that the benefits of this feature outweigh the cost. Still I think anyone, but library authors should avoid using it.
Tags:   english programming 
Posted by:   Stilgar
01:10 04.11.2019

Comments:



No comments yet.




Post as:



Post a comment: