這個做法建議只在介面上定義必要的功能,剩下需要的功能新增在這個介面上的擴充方法就好。 例如 System.Linq.Enumerable 就是個很好的例子,它幫 IEnumerable<T> 介面定義了許多常用的擴充方法,例如 Where、OrderBy、ThenBy。

像下面這段程式碼就是幫 IComparable 建立了一組擴充方法這樣在使用的時候可以提升可讀性。

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

不過要注意假如已經對某個介面定義了擴充方法,之後其他類型又想要實做自己的方法,這樣會產生問題。 例如下面這個範例在 IFoo 介面上定義擴充方法 FooExtensions.NextMarker(),輸出的結果為 1

void Main()
{
	MyType t = new MyType();
	UpdateMarker(t);
	Console.WriteLine(t.Marker);
}

public static void UpdateMarker(MyType foo) => foo.NextMarker();
public class MyType : IFoo
{
	public int Marker { get; set; }
}

public interface IFoo
{
	int Marker { get; set; }
}

public static class FooExtensions
{
	public static void NextMarker(this IFoo thing)
	{
		thing.Marker += 1;
	}
}

但是之後某人在自己的類別也實做了 NextMarker(),會導致輸出結果被錯誤的修改成 5

public class MyType : IFoo
{
	public int Marker { get; set; }
	
	public void NextMarker() => Marker += 5;
}

這個問題沒辦法完全避免,使用者在開發的時候應該自行確保兩邊的方法的行為是相同的,或者是搭配 unit test 才不會影響程式運行。


Summary

如果有很多 class 需要實做你寫的介面,那麼在定義介面的時候就應該只定義必要的方法,等之後透過擴充方法的方式來編寫需要的方法, 這樣不僅可以讓實做介面的使用者少寫一些程式碼,也能讓使用介面的人使用我們寫的擴充方法。