C# ジェネリックインターフェース

ジェネリックインターフェースは、型パラメータを持つインターフェースです。IEnumerable<T>IComparable<T> など、.NET の基盤となる多くのインターフェースがジェネリックとして定義されています。

基本的な定義方法

インターフェース名の後に <T> を付けることで、ジェネリックインターフェースになります。

public interface IRepository&lt;T&gt;
{
    T GetById(int id);
    void Add(T entity);
    void Delete(T entity);
    IEnumerable&lt;T&gt; GetAll();
}

ジェネリックインターフェースの実装

実装するクラスでは、具体的な型を指定するか、クラス自体もジェネリックにします。

// 具体的な型を指定して実装
public class UserRepository : IRepository&lt;User&gt;
{
    private List&lt;User&gt; _users = new();

    public User GetById(int id) =&gt; _users.FirstOrDefault(u =&gt; u.Id == id)!;
    public void Add(User entity) =&gt; _users.Add(entity);
    public void Delete(User entity) =&gt; _users.Remove(entity);
    public IEnumerable&lt;User&gt; GetAll() =&gt; _users;
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

クラス自体をジェネリックにする方法もあります。

// ジェネリッククラスとして実装
public class GenericRepository&lt;T&gt; : IRepository&lt;T&gt;
{
    private List&lt;T&gt; _items = new();

    public T GetById(int id) =&gt; _items.ElementAtOrDefault(id)!;
    public void Add(T entity) =&gt; _items.Add(entity);
    public void Delete(T entity) =&gt; _items.Remove(entity);
    public IEnumerable&lt;T&gt; GetAll() =&gt; _items;
}

.NET 標準のジェネリックインターフェース

.NET には多くの標準ジェネリックインターフェースがあります。

IEnumerable<T>

コレクションの反復処理を可能にする。foreach で使用される。

IComparable<T>

同じ型のオブジェクト同士の比較を定義する。

IEquatable<T>

同じ型のオブジェクト同士の等価性を定義する。

ICollection<T>

コレクションの基本操作(追加、削除、カウント)を定義する。

IComparable<T> の実装例

オブジェクトの並び替えを可能にするには IComparable<T> を実装します。

public class Product : IComparable&lt;Product&gt;
{
    public string Name { get; set; } = "";
    public decimal Price { get; set; }

    public int CompareTo(Product? other)
    {
        if (other == null) return 1;
        return Price.CompareTo(other.Price);
    }
}
var products = new List&lt;Product&gt;
{
    new() { Name = "Apple", Price = 150 },
    new() { Name = "Banana", Price = 100 },
    new() { Name = "Cherry", Price = 300 }
};

products.Sort(); // IComparable&lt;Product&gt; を使って並び替え

foreach (var p in products)
    Console.WriteLine($"{p.Name}: {p.Price}円");
// Banana: 100円
// Apple: 150円
// Cherry: 300円

制約としてのジェネリックインターフェース

ジェネリックインターフェースは制約として使うことで、型の機能を保証できます。

public static T FindMax&lt;T&gt;(IEnumerable&lt;T&gt; items) where T : IComparable&lt;T&gt;
{
    T max = items.First();
    foreach (var item in items.Skip(1))
    {
        if (item.CompareTo(max) &gt; 0)
            max = item;
    }
    return max;
}

var numbers = new[] { 3, 1, 4, 1, 5 };
Console.WriteLine(FindMax(numbers)); // 5

ジェネリックインターフェースを活用することで、型安全かつ柔軟な抽象化が実現できます。