C#もKotlinと同様にジェネリクスが存在しますが、Kotlin/Javaよりも高性能なジェネリクスが搭載されています。Kotlinでは型パラメーターの情報は実行時には削除されますが、C#では型パラメーターの情報を保持しているため、Kotlinのジェネリクスにはできないことができます。 たとえば、型パラメーターの型へのキャストなど。 また、構造体にも対応しているため、Javaのようにboxingする必要がありません。
基本的にはKotlinと同様な記法になっています。
class Value<T> { public Value(T value) { } } class User { public void Main() { var value = new Value<string>(string.Empty); } }
Variance
C#のジェネリクスには共変性と反変性があります。
共変性は型パラメーターにout
修飾子を付けることで宣言します。out
修飾子を付けた型パラメーターは出力(返り値など)にしか使えなくなります。
interface IValue<out T> { T Main(); } class Value<T> : IValue<T> { public T Main() { // 参照型の場合はnull、値型の場合は既定値を返す特別なキーワード return default; } } class User { public void Main() { IValue<string> stringValue = new Value<string>(); // 共変性があるので型パラメーターのアップキャストができる IValue<object> objectValue = stringValue; } }
反変性は型パラメーターにin
修飾子を付けることで宣言します。in
修飾子を付けた型パラメーターは入力(引数など)の型にしか使えなくなります。
interface IValue<in T> { void Main(T value); } class Value<T> : IValue<T> { public void Main(T value) { } } class User { public void Main() { IValue<object> objectValue = new Value<object>(); // 反変性があるので型パラメーターのダウンキャストができる IValue<string> stringValue = objectValue; } }
また、型パラメーターにはwhere
によってconstraint(制約)を付けることができます。
たとえば、型パラメーターに対して基底型の制約を付ける付けることができます。
interface IStatus { } interface IUser<TStatus> where TStatus : IStatus { }
また、Kotlin/JavaにはなくてC#にはある制約の強みとして、引数0のコンストラクターが存在する制約も付けることができます。
interface IStatus { } interface IUser<TStatus> where TStatus : IStatus, new() { }
これ以外にも構造体などの制約を加えることができますが、詳しくはドキュメントを参照してください。
interface IValue<T> where T : struct { }
Declaration-site variance
説明済みのような気がするので割愛。
Type projections
おそらくC#にはない機能のことを説明してるので割愛。
というか、共変性と反変性が該当する機能かも。
Generic functions
C#にもジェネリクスメソッドがあります。 機能はあまり変わらないので説明略。
class Value { public void Sub<T>(T value) { } public void Main() { Sub<string>(string.Empty); // 引数から型パラメーターを型推論できる Sub(string.Empty); } }
Generic constraints
説明済みなので割愛。
Type erasure
Kotlin特有の型チェックなので割愛。