Default Interface Methods in C#: Love 'em or Hate 'em?
Tags: csharp, csadvent
Published: 2019-12-04 20:44:48

>This post is part of the [2019 C# Advent Calendar](https://crosscuttingconcerns.com/The-Third-Annual-csharp-Advent). Check out the link for other great posts!

I don't know I've ever run across a feature in C# that brought the immediate question, "Why would they do that?", but such was the case with almost everyone I talked to about one of the new features in C# 8.0, default interface methods. Wait... what?? Did he just say you can now add implementations in interfaces?

That's right! With C# 8.0, we can now add members to interfaces and implementations for those members. This means interfaces are no longer just a contract with declarations of methods, properties, indexers and events, but can now have private members, static members, protected members, virtual members and default implementations for these members.

When I first heard about this new feature, I was excited... until I wasn't. More on that in a minute. I finally had a way to update an interface to include a new method, one at least I was happy with.

Let's take a look at a real world example where I recently ran in to a situation perfectly suited for this new feature. I was creating an interactive chat bot for my Twitch stream, [Developers Garage](https://twitch.tv/developersgarage). 

# Real-World Example

The chat bot acts on commands entered by the viewers. In order to ease the addition of new commands, the common elements of the commands were extracted to the interface below and all commands implement this interface.

```csharp
    public interface IChatCommand
    {
        IEnumerable<string> Command { get; }

        string Description { get; }

        TimeSpan? Cooldown { get; }

        Task Execute(IChatService service, CommandArgs args);
    }
```

As new features were added, I realized there were commands I wanted to be executed by me only and I didn't want them to be able to be seen as available commands by viewers. My solution–add a new method returning whether or not a command can be listed.

```csharp
    bool CanBeListed();
```

Before we look at how the new default interface method feature helped us implement this solution, let's look at how we could have done it.

## The Old Way

To add this new method before, we had 2 options: 
1. Update the existing interface and then update all classes implementing this interface.
2. Create a new interface with the new method and have it inherit from the original interface.

```csharp
    public interface IChatCommand
    {
        IEnumerable<string> Command { get; }

        string Description { get; }

        TimeSpan? Cooldown { get; }

        Task Execute(IChatService service, CommandArgs args);
    }

    public interface IChatCommandExtended : IChatCommand
    {        
        bool CanBeListed();
    }
```
The second option is typically the preferred method, but as you continue to extend the interface, the list can grow to be unmanageable. The new default implementation feature in C# 8.0 alleviates the never ending cycle of interface extension.

## The New Way

With this new feature, we no longer have to create an extended interface, but add our new method to the existing interface and provide it a default implementation. 

```csharp
    public interface IChatCommand
    {
        IEnumerable<string> Command { get; }

        string Description { get; }

        TimeSpan? Cooldown { get; }       

        bool CanBeListed() => true;

        Task Execute(IChatService service, CommandArgs args);
    }

    public class ProjectCommand : IChatCommand
    {
        public IEnumerable<string> Command { get; } => "project";

        public string Description { get; } => "Returns current project";

        public TimeSpan? Cooldown { get; } => TimeSpan.FromSeconds(10);

        public Task Execute(IChatService service, CommandArgs args)
        {
            await service.SendMessage(currentProject);
        }
    }
```

This will prevent existing `IChatCommand` implementations from breaking while still providing an implementation when called upon by other code. 

And with every new command created we have the option of overriding the default implementation and hiding our new command from being listed.

```csharp
    public class ProjectCommand : IChatCommand
    {
        public IEnumerable<string> Command { get; } => "project";

        public string Description { get; } => "Returns current project";

        public TimeSpan? Cooldown { get; } => TimeSpan.FromSeconds(10);      

        public bool CanBeListed() => false;

        public Task Execute(IChatService service, CommandArgs args)
        {
            await service.SendMessage(currentProject);
        }
    }
```

# So Why Aren't You Excited Anymore?

I mentioned earlier in the post I wasn't as excited about the new default interface methods feature as I was when I first heard about it, and that's because the code generation action within Visual Studio doesn't generate these methods. 

I use this quick action a LOT when I'm developing and having to remember there is a default implementation on an interface is just asking for trouble. You can also turn it around and if a new member of the team, or junior developer, someone not super familiar with the code base, could totally gloss over it and find themselves spinning their wheels trying to figure out what's going on.

## So What's The Solution

Yes, I know I'm being a little nit picky and this doesn't really have anything to do with the new feature, but that's because I believe this is a great addition to C#. The tooling, in my opinion, is one of the things that make writing C# so enjoyable. So, I keep it to just as high a standard as I do the C# language. 

My solution would be to make the code generation action configurable by adding an option to automatically implement default implementations.

# Conclusion

I am a fan of default interface methods feature and based on conversations I've had with other developers, I'm one of the few. There are other scenarios this new feature can solve and you can find out more about them in the documentation [here](https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-methods-versions). 

They're a lot more great features brought to us in C# 8.0 and I encourage you to go check them out [here](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8).

What are some of your favorite, or disliked, features in C# 8.0? Let me know in the comments below.