[C#] Defining Default Behavior (Default Implementation) for Methods in Interfaces

Before C# 8.0, interfaces were strictly for defining “contracts” and could not contain concrete processing (implementation). However, with the language specification extensions in C# 8.0, it became possible to define “Default Interface Methods”.

Using this feature allows you to add new methods to an interface without modifying existing implementation classes, or to centralize common logic within the interface itself.

This article explains how to implement this and the precautions to take, using a logic example that calculates point return rates based on member rank.

目次

What is a Default Interface Implementation?

Normally, if you add a method to an interface, a compilation error (“Method not implemented”) occurs in all classes that implement that interface.

However, if you define the body of the method on the interface side, that processing is automatically used unless the implementation class overrides that method. This makes it easy to maintain compatibility when upgrading APIs (adding functionality without breaking existing code).

Practical Code Example: Point Calculation by Member Rank

The following code defines the logic for point calculation in the member interface (IMember). The RegularMember class does not implement this method, so it uses the interface’s default implementation to perform the calculation.

using System;

namespace MembershipSystem
{
    // Member Interface
    public interface IMember
    {
        string Name { get; }
        int Rank { get; }

        // C# 8.0+: You can describe the implementation of the method in the interface.
        // If not implemented (overridden) by the class, this logic is used.
        decimal CalculatePointRate()
        {
            return Rank switch
            {
                1 => 0.01m, // Rank 1: 1%
                2 => 0.05m, // Rank 2: 5%
                3 => 0.10m, // Rank 3: 10%
                _ => 0.0m   // Others: 0%
            };
        }
    }

    // Regular Member Class
    // Does not implement CalculatePointRate, but causes no compile error.
    public class RegularMember : IMember
    {
        public string Name { get; set; }
        public int Rank { get; set; }
    }

    // VIP Member Class
    // If the default implementation is insufficient, you can implement (redefine) it yourself.
    public class VipMember : IMember
    {
        public string Name { get; set; }
        public int Rank { get; set; }

        // Apply custom logic instead of default implementation
        public decimal CalculatePointRate()
        {
            return 0.20m; // Flat 20%
        }
    }

    class Program
    {
        static void Main()
        {
            // IMPORTANT: Variable type must be the Interface type (IMember).
            IMember member1 = new RegularMember { Name = "Tanaka", Rank = 2 };
            IMember member2 = new VipMember { Name = "Suzuki", Rank = 3 };

            // member1 has no implementation in RegularMember, so IMember's default implementation is called.
            Console.WriteLine($"{member1.Name} (Rank {member1.Rank}): {member1.CalculatePointRate():P0}");

            // member2 prioritizes the implementation in VipMember.
            Console.WriteLine($"{member2.Name} (Rank {member2.Rank}): {member2.CalculatePointRate():P0}");
        }
    }
}

Execution Result

Tanaka (Rank 2): 5%
Suzuki (Rank 3): 20%

Technical Points and Important Constraints

1. Casting to Interface Type is Required

The most important point to note when using this feature is that “Default implementations can only be called when the variable type is the interface type.”

If you declare a variable as the class type as shown below, a compilation error will occur.

// Error because the CalculatePointRate method does not exist on the RegularMember class itself
RegularMember member = new RegularMember();
// member.CalculatePointRate(); // Compilation Error!

// Callable if explicitly cast to the interface
((IMember)member).CalculatePointRate(); // OK

This is because the default implementation is not inherited as part of the class’s public interface (class members).

2. Difference from Abstract Classes

Now that interfaces can have implementations, the difference from abstract classes might feel ambiguous, but there are clear distinctions:

  • Interface: Cannot hold state (field data). You can define properties, but the fields holding the values must be in the implementation class. Multiple inheritance (multiple implementation) is possible.
  • Abstract Class: Can hold state (fields). Multiple inheritance is not possible.

3. Application to the Traits Pattern

This feature makes patterns similar to “Traits” found in other languages possible in C#. A design approach where multiple interfaces hold small functionalities (method implementations) and classes are composed by combining them becomes a valid option.

Summary

Default interface implementation is a very powerful feature for library designers. It allows for functional expansion by adding methods to released interfaces later without breaking the code on the usage side. However, please implement with the understanding that calling these methods requires treating the object as the interface type.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次