これは、言語デザイン、パターン、およびセマンティクスについての難しい質問です。実用的な価値が見当たらないという理由だけで反対票を投じないでください。
まず、関数とそのパラメータについて考えてみましょう。次に、関数とそのパラメーター/引数と、ジェネリッククラス/関数とその型パラメーター/型引数との類似点を見ていきます。
関数は、「パラメータ」と呼ばれるいくつかの不特定の値を持つコードのブロックです。引数を指定して結果を受け取ります。
ジェネリッククラスは、いくつかの不特定の「タイプパラメータ」を持つクラスです。type -argumentsを指定すると、クラスを操作できます。コンストラクターを呼び出すか、静的メソッドを呼び出します。
非ジェネリッククラスのジェネリック関数は、いくつかの不特定の「型パラメーター」といくつかの不特定の「値パラメーター」を持つ関数です。結果を受け取るために、型引数と値引数を指定します。
デリゲートは、特定の機能へのポインターです。デリゲートを作成するときは、関数の引数を指定せず、後で指定します。
問題は、.Netには、ジェネリック型パラメーターが指定されていないジェネリック関数のデリゲートに相当するものがないことです。後でtype-parametersにtype-valuesを指定することはできません。自由値パラメーターだけでなく、自由型パラメーターも持つデリゲートを想像することができます。
static class SomeClass {
//generic function
public static T GetValue<T>() {
return default(T);
}
}
//creating delegate to generic function or method group
Func{TFree}<TFree> valueFactory = SomeClass.GetValue;
//creating delegate to anonymous generic function
Func{TFree}<int, List<TFree>> listFactory = {TFree}(int capacity) => new List<TFree>(capacity);
以下は、C#で記述したいプログラムの[擬似]コードです。正しいC#プログラムで同様の動作を実現する方法を知りたいです。
C#で無料のジェネリック型パラメーターを使用してデリゲートをエミュレートするにはどうすればよいですか?
非ジェネリックコードを介して、まだ未知のジェネリックパラメータを持つジェネリック関数への参照/リンクをどのように渡すことができますか?
public static class Factory { //Everything compiles fine here
public delegate ICollection<T> FactoryDelegate<T>(IEnumerable<T> values);
public static ICollection<T> CreateList<T>(IEnumerable<T> values) {
return new List<T>(values);
}
public static ICollection<T> CreateSet<T>(IEnumerable<T> values) {
return new HashSet<T>(values);
}
}
public class Worker { //non-generic class
Func{TFree}<FactoryDelegate<TFree>> _factory; //TFree is a "free" generic type paramenter
public Worker(Func{TFree}<FactoryDelegate<TFree>> factory) {
_factory = factory;
}
public ICollection<T> DoWork<T>(IEnumerable<T> values) { //generic method
return _factory{T}(values); //supplying T as the argument for type parameter TFree
}
}
public static class Program {
public static void Main() {
string[] values1 = new string[] { "a", "b", "c" };
int[] values2 = new int[] { 1, 2, 2, 2 };
Worker listWorker = new Worker(Factory.CreateList); //passing reference to generic function
Worker setWorker = new Worker(Factory.CreateSet); //passing reference to generic function
ICollection<string> result1 = listWorker.DoWork(values1);
ICollection<int> result2 = listWorker.DoWork(values2); //.Count == 4
ICollection<int> result3 = setWorker.DoWork(values2); //.Count == 2
}
}
型引数を指定せずに、ジェネリック関数(Factory.CreateListおよびFactory.CreateSet)への参照をWorkerクラスコンストラクターに渡す方法をご覧ください。型引数は、後で汎用DoWork関数が具象型配列で呼び出されたときに提供されます。DoWorkは、type-argumentsを使用して正しい関数を選択し、value-argumentsをそれに渡し、受け取った値を返します。