BaseFruit
にint weight
を受け入れるコンストラクターがある場合、このような汎用メソッドでフルーツのインスタンスを作成できますか?
public void AddFruit<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
コメントの後ろに例が追加されています。 BaseFruit
にパラメーターなしのコンストラクターを指定し、メンバー変数を使用してすべてを入力する場合にのみ、これを実行できるようです。私の実際のコード(フルーツに関するものではありません)では、これはかなり非現実的です。
-Update-
だから、どういうわけか制約によって解決できないようです。回答から、3つの候補ソリューションがあります。
リフレクションは最もクリーンでないと思う傾向がありますが、他の2つを決定することはできません。
さらに簡単な例:
return (T)Activator.CreateInstance(typeof(T), new object[] { weight });
Tでnew()制約を使用するのは、コンパイラがコンパイル時にパブリックパラメータレスコンストラクターをチェックするためだけであり、型の作成に使用される実際のコードはActivatorクラスであることに注意してください。
既存の特定のコンストラクターについて確認する必要がありますが、この種の要件はコードの匂いかもしれません(または、c#の現在のバージョンでは避けるべきものです)。
パラメーター化されたコンストラクターは使用できません。 「where T : new()
」制約がある場合は、パラメーターなしのコンストラクターを使用できます。
それは痛みですが、それは人生です:(
これは "static interfaces" で対処したいことの1つです。その後、静的メソッド、演算子、およびコンストラクターを含めるようにTを制約し、それらを呼び出すことができます。
はい;場所を変更します。
where T:BaseFruit, new()
ただし、これはparameterlessコンストラクターでのみ機能します。プロパティを設定する他の方法(プロパティ自体または同様の設定)が必要です。
最も簡単なソリューションActivator.CreateInstance<T>()
Jonが指摘したように、これは非パラメーターレスコンストラクターを制約するための人生です。ただし、別の解決策はファクトリパターンを使用することです。これは簡単に制約されます
interface IFruitFactory<T> where T : BaseFruit {
T Create(int weight);
}
public void AddFruit<T>( IFruitFactory<T> factory ) where T: BaseFruit {
BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
さらに別のオプションは、機能的なアプローチを使用することです。ファクトリメソッドを渡します。
public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit {
BaseFruit fruit = factoryDel(weight); /* new Apple(150); */
fruit.Enlist(fruitManager);
}
リフレクションを使用して行うことができます。
public void AddFruit<T>()where T: BaseFruit
{
ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
if (constructor == null)
{
throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
}
BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
fruit.Enlist(fruitManager);
}
編集: コンストラクター== nullチェックを追加しました。
編集: キャッシュを使用した高速バリアント:
public void AddFruit<T>()where T: BaseFruit
{
var constructor = FruitCompany<T>.constructor;
if (constructor == null)
{
throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
}
var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
fruit.Enlist(fruitManager);
}
private static class FruitCompany<T>
{
public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
}
このメソッドを作成しました:
public static V ConvertParentObjToChildObj<T,V> (T obj) where V : new()
{
Type typeT = typeof(T);
PropertyInfo[] propertiesT = typeT.GetProperties();
V newV = new V();
foreach (var propT in propertiesT)
{
var nomePropT = propT.Name;
var valuePropT = propT.GetValue(obj, null);
Type typeV = typeof(V);
PropertyInfo[] propertiesV = typeV.GetProperties();
foreach (var propV in propertiesV)
{
var nomePropV = propV.Name;
if(nomePropT == nomePropV)
{
propV.SetValue(newV, valuePropT);
break;
}
}
}
return newV;
}
私はそれをこのように使用します:
public class A
{
public int PROP1 {get; set;}
}
public class B : A
{
public int PROP2 {get; set;}
}
コード:
A instanceA = new A();
instanceA.PROP1 = 1;
B instanceB = new B();
instanceB = ConvertParentObjToChildObj<A,B>(instanceA);
User1471935の提案への追加として:
1つ以上のパラメーターを持つコンストラクターを使用して汎用クラスをインスタンス化するために、Activatorクラスを使用できるようになりました。
T instance = Activator.CreateInstance(typeof(T), new object[] {...})
オブジェクトのリストは、提供するパラメーターです。 Microsoftによると :
CreateInstance [...]は、指定されたパラメーターに最も一致するコンストラクターを使用して、指定されたタイプのインスタンスを作成します。
また、CreateInstanceの汎用バージョン(CreateInstance<T>()
)もありますが、コンストラクターパラメーターを指定することもできません。
最近、非常によく似た問題に出会いました。ソリューションを皆さんと共有したかっただけです。を使用して、jsonオブジェクトからCar<CarA>
のインスタンスを作成したかった。
Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>();
mapper.Add(1, typeof(CarA));
mapper.Add(2, typeof(BarB));
public class Car<T> where T : class
{
public T Detail { get; set; }
public Car(T data)
{
Detail = data;
}
}
public class CarA
{
public int PropA { get; set; }
public CarA(){}
}
public class CarB
{
public int PropB { get; set; }
public CarB(){}
}
var jsonObj = {"Type":"1","PropA":"10"}
MyEnum t = GetTypeOfCar(jsonObj);
Type objectT = mapper[t]
Type genericType = typeof(Car<>);
Type carTypeWithGenerics = genericType.MakeGenericType(objectT);
Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) });