trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

EFFECTIVE C# 6.0/7.0 読書メモ 項目27

項目27 最小限に制限されたインターフェイスを拡張メソッドにより既往拡張する

インターフェイスには最小限の機能を定義しておき、拡張メソッドを用意するようにする。

インターフェイスとして定義されたメンバを最小限に抑え、 補助的な機能を拡張メソッドとして定義する。こうすると、 インターフェイスを実装する側では作業量を最小にでき、インターフェイスを使用する側の利便性を最大化できる。

例えばSystem.Linq.Enumerableクラス。 System.EnumerableにはIEnumerable<T>に対する拡張メソッドが定義されている(WhereOrderByThenByGroupintoなど)。

IEnumerable<T>に対して拡張メソッドを定義すると、IEnumerable<T>を実装しているクラスを変更せずに機能を追加でき、すべてのコレクションに対してクエリ演算を行うことができる。

利用例

IComparable<T>を直感的にするため、 left.LessThan(right),left.GreaterThanEqual(right) を追加する。

public static class Comparable
{
    public static bool LessThan<T>(this T left, T right)
        where T : IComparable<T> =>left.CompareTo(right)<0;
    //略
}

インターフェイスを実装する側では1つのメソッド(CompareTo)だけの実装でよい。使用する側では直感的にわかりやすいメソッドとして呼び出すことができる。

アプリケーションで独自に定義するインターフェイスに対しても同様に、必要最小限の機能だけをインターフェイスに定義し、補助的なメソッドを用意する場合には拡張メソッドとして実装するとよい。

注意点

拡張メソッドよりクラスに定義されたメソッドのほうが優先される。インターフェイスを使用するようになっているコードの場合、型に定義されたメソッドよりも拡張メソッドの方が優先される。

public interface IFoo { }
public static class FooExtensions
{
    public static void NextMarker(this IFoo thing) { }
}
class MyType : IFoo
{
    public void UpdateMarker() => this.NextMarker();//拡張メソッドのNextMarkerが呼ばれる
}
class MyType2 : IFoo
{
    public void NextMarker() => Marker += 5;
    public void UpdateMarker() => this.NextMarker();//型に定義されたメソッドが呼ばれる
}
class MyType3 : IFoo
{
    public void NextMarker() => Marker += 5;
    public void UpdateMarker() => ((IFoo)this).NextMarker();//拡張メソッドのNextMakerが呼ばれる
}