C#では、次のような汎用メソッドに制約を設定できます。
public class A {
public static void Method<T> (T a) where T : new() {
//...do something...
}
}
T
には、パラメーターを必要としないコンストラクターが必要であることを指定します。 「float[,]
パラメータを持つコンストラクタが存在しますか?」のような制約を追加する方法があるかどうか疑問に思っています
次のコードはコンパイルされません。
public class A {
public static void Method<T> (T a) where T : new(float[,] u) {
//...do something...
}
}
回避策も役立ちますか?
あなたが見つけたように、これを行うことはできません。
回避策として、通常T
型のオブジェクトを作成できるデリゲートを提供します。
public class A {
public static void Method<T> (T a, Func<float[,], T> creator) {
//...do something...
}
}
そのような構造はありません。空のコンストラクター制約のみを指定できます。
ラムダメソッドでこの問題を回避します。
public static void Method<T>(Func<int,T> del) {
var t = del(42);
}
使用事例
Method(x => new Foo(x));
リフレクションを使用してジェネリックオブジェクトを作成しても、型は宣言された正しいコンストラクターを必要とします。そうしないと、例外がスローされます。コンストラクターのいずれかと一致する限り、任意の引数を渡すことができます。
この方法を使用すると、テンプレート内のコンストラクターに制約を設定できません。コンストラクターがない場合、コンパイル時にエラーを取得するのではなく、実行時に例外を処理する必要があります。
// public static object CreateInstance(Type type, params object[] args);
// Example 1
T t = (T)Activator.CreateInstance(typeof(T));
// Example 2
T t = (T)Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...);
// Example 3
T t = (T)Activator.CreateInstance(typeof(T), (string)arg0, (int)arg1, (bool)arg2);
私は個人的に非常に効果的だと思うこれの回避策を以下に示します。一般的なパラメーター化されたコンストラクター制約とは何かを考えると、実際には、特定のシグネチャを持つ型とコンストラクターの間のマッピングです。辞書を使用して、このような独自のマッピングを作成できます。これらを静的な「ファクトリ」クラスに入れると、毎回コンストラクタラムダを構築することを心配することなく、さまざまなタイプのオブジェクトを作成できます。
public static class BaseTypeFactory
{
private delegate BaseType BaseTypeConstructor(int pParam1, int pParam2);
private static readonly Dictionary<Type, BaseTypeConstructor>
mTypeConstructors = new Dictionary<Type, BaseTypeConstructor>
{
{ typeof(Object1), (pParam1, pParam2) => new Object1(pParam1, pParam2) },
{ typeof(Object2), (pParam1, pParam2) => new Object2(pParam1, pParam2) },
{ typeof(Object3), (pParam1, pParam2) => new Object3(pParam1, pParam2) }
};
次に、一般的なメソッドで、たとえば:
public static T BuildBaseType<T>(...)
where T : BaseType
{
...
T myObject = (T)mTypeConstructors[typeof(T)](value1, value2);
...
return myObject;
}
いいえ。現時点で指定できるコンストラクター制約は、引数なしのコンストラクターのみです。
これは、オブジェクトの構築方法に何らかの制約を課す最もクリーンなソリューションだと思います。コンパイル時に完全にチェックされるわけではありません。クラスの実際のコンストラクターにIConstructorインターフェイスのような同じシグネチャを持たせることに同意した場合、それは一種のコンストラクターの制約を持つようなものです。 Constructor
メソッドは、明示的なインターフェイス実装のため、オブジェクトを通常に使用している場合は非表示になります。
using System.Runtime.Serialization;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var employeeWorker = new GenericWorker<Employee>();
employeeWorker.DoWork();
}
}
public class GenericWorker<T> where T:IConstructor
{
public void DoWork()
{
T employee = (T)FormatterServices.GetUninitializedObject(typeof(T));
employee.Constructor("John Doe", 105);
}
}
public interface IConstructor
{
void Constructor(string name, int age);
}
public class Employee : IConstructor
{
public string Name { get; private set; }
public int Age { get; private set; }
public Employee(string name, int age)
{
((IConstructor)this).Constructor(name, age);
}
void IConstructor.Constructor(string name, int age)
{
Name = name;
Age = age;
}
}
}
制約付きのジェネリッククラスを作成する方法については、ここでは、値と参照の型を持つ構造体とクラスを選択しました。
そのようにして、コンストラクタは値に制約を持ちます。
class MyGenericClass<T, X> where T :struct where X: class { private T genericMemberVariableT; private X genericMemberVariableX; public MyGenericClass(T valueT, X valueX) { genericMemberVariableT = valueT; genericMemberVariableX = valueX; } public T genericMethod(T genericParameter) { Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter); Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariableT); Console.WriteLine("Return type: {0}, value: {1}", typeof(X).ToString(), genericMemberVariableX); return genericMemberVariableT; } public T genericProperty { get; set; } }
実装:
MyGenericClass<int, string> intGenericClass = new MyGenericClass<int, string>(10, "Hello world"); int val = intGenericClass.genericMethod(200);