Here’s a cool feature of C# 8 that I found use for in an application I’m developing. In my context, it’s useful because I have both a few types of things that share logic, as well as patterns that can apply in any combination to multiple things that have fixed relationships to other implementations.

I can’t find the original example that put me onto this possibility, but here is a similar blog post detailing their own example - C# Default Interface Methods

Let’s say you were starting a bakery and want to make lots of cakes. I’m using cakes as my model example here, because I’m hungry. Also it gives me a great excuse for using Great British Bake Off gifs. Cake Week on Great British Bake Off

Your bakery needs to offer a wide variety of cakes that each have their own recipe and structure:

  1. butter
  2. pound
  3. sponge
  4. genoise
  5. biscuit
  6. angel food
  7. chiffon
  8. red velvet

GBBO Contestant - “I’ve Cried Over Cake”

You have three types of frosting used across those varieties of cake. It doesn’t define the cake, but it pairs well with the form. Someday you might want to change this, but it’s expected to be pretty stable, because ganache doesn’t work well on biscuit cake and angel food cake without whipped cream is just a waste. Here are the types of frosting:

  1. Buttercream
  2. Whipped cream
  3. Ganache

Fighting with frosting on GBBO

So we know what we’re baking, how can we define this in code? I don’t want to have a bunch of duplicate logic and processes for my different types of frosting, and they have a relationship to the type of cake which I want to formalize. C# has single inheritance, and if I’m going to have an abstract cake class it makes the most sense for this to be extended to concrete cakes.

Static interface methods to the rescue. Historically C# interfaces don’t have any logic themselves. However, as of C# 8 you can define a static method on an interface that can be referenced and used by classes that implement that interface.

Okay, so lets setup an abstract cake:

    public abstract class Cake
    {
        public abstract string CakeName { get; set; }

        public abstract string Decorate();
    }

I’m going to make some interfaces for my frosting types, which will have their own concrete implementation of an abstract ApplyFrostingMethod (you might pour ganache, but spread buttercream) like so, by referencing both the concrete apply method and providing a Frost implementation using a property set on the class that implements this interface.

    internal interface IGanacheFrosting
    {
        public string FrostingStartPattern { get; }
        public static ApplyFrostingMethod ApplyFrosting => new PourFrosting();
        protected static string Frost(IGanacheFrosting s)
        {
            return s.FrostingStartPattern + ApplyFrosting.Apply();
        }
    }

    public abstract class ApplyFrostingMethod
    {
        public abstract string Apply();
    }

    public class PourFrosting : ApplyFrostingMethod
    {
        public override string Apply()
        {
            return "~~~~~~~~~~~~~~~~~~";
        }
    }

    public class SpreadFrosting : ApplyFrostingMethod
    {
        public override string Apply()
        {
            return "//////////////////";
        }
    }

And then we want to extend it to a concrete type of cake - we can’t have some platonic Cake, but we can have a butter cake (and eat it too!)

    public class ButterCake : Cake, IGanacheFrosting
    {
        public override string CakeName { get; set; } = "Butter Cake";
        public string FrostingStartPattern => "*@*";

        public override string Decorate()
        {
            return IGanacheFrosting.Frost(this);
        }
    }

Now here using that setup, I can do something similar to apply whipped cream to my angel food cake.

    public class AngelFoodCake : Cake, IWhippedCreamFrosting
    {
        public override string CakeName { get; set; } = "Strawberry Angel Food Cake";
        public string FrostingColor => "Pink";

        public override string Decorate()
        {
            return IWhippedCreamFrosting.Frost(this);
        }
    }

    internal interface IWhippedCreamFrosting
    {
        //we might add food die to whipped cream unique to the type of cake!
        public string FrostingColor { get; } 
        public static ApplyFrostingMethod ApplyFrosting => new SpreadFrosting();
        protected static string Frost(IWhippedCreamFrosting f)
        {
            return $"{ApplyFrosting.Apply()} in {f.FrostingColor}";
        }
    }

And last but not least, if someday we decide to offer our red velvet cake with either, that is possible using interfaces as well

    public class PoundCake : Cake, IWhippedCreamFrosting, IGanacheFrosting
    {
        public override string CakeName { get; set; } = "Pound Cake";
        public string FrostingColor => "Orange";

        public string FrostingStartPattern => ">>!!<<";

        public int FrostingType { get; set; }

        public override string Decorate()
        {
            if(FrostingType == 1)
            {
                return IWhippedCreamFrosting.Frost(this);
            }
            return IGanacheFrosting.Frost(this);
        }
    }

Paul from GBBO saying “Piece of cake”