C# 複数の型パラメータを持つジェネリクス
ジェネリクスでは複数の型パラメータを持つクラスやメソッドを定義できます。Dictionary<TKey, TValue> はその代表例で、キーと値それぞれに異なる型を指定できます。
複数の型パラメータを持つクラス
型パラメータはカンマで区切って複数指定します。
public class KeyValuePair<TKey, TValue>
{
public TKey Key { get; }
public TValue Value { get; }
public KeyValuePair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}var pair1 = new KeyValuePair<string, int>("Age", 25);
var pair2 = new KeyValuePair<int, bool>(1, true);
Console.WriteLine($"{pair1.Key}: {pair1.Value}"); // Age: 25実用的な例:Result 型
成功/失敗を表現する Result 型は、複数の型パラメータの良い例です。
public class Result<TSuccess, TError>
{
public TSuccess? SuccessValue { get; }
public TError? ErrorValue { get; }
public bool IsSuccess { get; }
private Result(TSuccess? success, TError? error, bool isSuccess)
{
SuccessValue = success;
ErrorValue = error;
IsSuccess = isSuccess;
}
public static Result<TSuccess, TError> Success(TSuccess value)
=> new(value, default, true);
public static Result<TSuccess, TError> Failure(TError error)
=> new(default, error, false);
}Result<int, string> Divide(int a, int b)
{
if (b == 0)
return Result<int, string>.Failure("ゼロで除算できません");
return Result<int, string>.Success(a / b);
}
var result = Divide(10, 2);
if (result.IsSuccess)
Console.WriteLine($"結果: {result.SuccessValue}");
else
Console.WriteLine($"エラー: {result.ErrorValue}");型パラメータ間の関係を制約で表現
型パラメータ同士に関係を持たせることもできます。
public class Converter<TInput, TOutput>
where TOutput : new()
{
private readonly Func<TInput, TOutput> _convertFunc;
public Converter(Func<TInput, TOutput> convertFunc)
{
_convertFunc = convertFunc;
}
public TOutput Convert(TInput input) => _convertFunc(input);
}3つ以上の型パラメータ
型パラメータは3つ以上でも問題ありません。ただし、多くなりすぎると可読性が下がります。
public class Triple<T1, T2, T3>
{
public T1 First { get; set; }
public T2 Second { get; set; }
public T3 Third { get; set; }
public Triple(T1 first, T2 second, T3 third)
{
First = first;
Second = second;
Third = third;
}
}
var triple = new Triple<string, int, bool>("Test", 42, true);型パラメータの命名
複数の型パラメータを持つ場合は、役割がわかる名前を付けると良いでしょう。
汎用的な命名
T1, T2, T3 のような連番。短いが意味が伝わりにくい。
説明的な命名
TKey, TValue, TInput, TOutput など。役割が明確になる。
// 良い例:役割がわかる名前
public class Transformer<TSource, TDestination> { }
public class Cache<TKey, TValue> { }
// 避けたい例:意味がわかりにくい
public class Processor<T1, T2, T3> { }複数の型パラメータを使いこなすことで、より汎用的で再利用性の高いコンポーネントを設計できます。