この質問にはすでに答えがあります。
実行時に決定される型のインスタンスを作成するための(.NET 4の)最善の方法は何ですか。
私はBaseClassオブジェクトに作用するけれどもその派生クラスのインスタンスによって呼ばれるかもしれないインスタンスメソッドを持っています。と同じタイプの別のインスタンスを作成する必要があります。this
メソッド内派生クラスごとにMethodをオーバーロードすることは、かなり複雑であり、単一の実装を維持する方が効率的であるため、実用的ではありません。
public class BaseClass
{
//constructors + properties + methods etc
public SomeMethod()
{
//some code
DerivedClass d = new DerivedClass(); //ideally determine the DerivedClass type at run-time
}
}
リフレクションやdynamicキーワードの使用について少し読んだことがありますが、これらについては経験がありません。
あなたは探しているActivator.CreateInstance
(他のオーバーロードもあります。これですそれはコンストラクタ引数を受け取ります)。だからあなたは書くことができます
var anotherOneLikeMe = Activator.CreateInstance(this.GetType());
ここに問題があるかもしれませんanotherOneLikeMe
次のように入力されますobject
ですから、あなたがそれを共通の基本クラスにキャストするつもりでない限り(例:BaseClass
あなたの例では)あなたがそれでできることはあまりありません。
実行時にインスタンスを繰り返し作成するパフォーマンスのための最良の方法は、コンパイル式です。
static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
X x = YCreator();
統計(2012):
Iterations: 5000000
00:00:00.8481762, Activator.CreateInstance(string, string)
00:00:00.8416930, Activator.CreateInstance(type)
00:00:06.6236752, ConstructorInfo.Invoke
00:00:00.1776255, Compiled expression
00:00:00.0462197, new
統計(2015、.net 4.5、x 64):
Iterations: 5000000
00:00:00.2659981, Activator.CreateInstance(string, string)
00:00:00.2603770, Activator.CreateInstance(type)
00:00:00.7478936, ConstructorInfo.Invoke
00:00:00.0700757, Compiled expression
00:00:00.0286710, new
統計(2015年、.net 4.5、x86):
Iterations: 5000000
00:00:00.3541501, Activator.CreateInstance(string, string)
00:00:00.3686861, Activator.CreateInstance(type)
00:00:00.9492354, ConstructorInfo.Invoke
00:00:00.0719072, Compiled expression
00:00:00.0229387, new
フルコード
public static X CreateY_New()
{
return new Y();
}
public static X CreateY_CreateInstance()
{
return (X)Activator.CreateInstance(typeof(Y));
}
public static X CreateY_CreateInstance_String()
{
return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}
static readonly System.Reflection.ConstructorInfo YConstructor =
typeof(Y).GetConstructor(Type.EmptyTypes);
static readonly object[] Empty = new object[] { };
public static X CreateY_Invoke()
{
return (X)YConstructor.Invoke(Empty);
}
static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
public static X CreateY_CompiledExpression()
{
return YCreator();
}
static void Main(string[] args)
{
const int iterations = 5000000;
Console.WriteLine("Iterations: {0}", iterations);
foreach (var creatorInfo in new []
{
new {Name = "Activator.CreateInstance(string, string)", Creator = (Func<X>)CreateY_CreateInstance},
new {Name = "Activator.CreateInstance(type)", Creator = (Func<X>)CreateY_CreateInstance},
new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke},
new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression},
new {Name = "new", Creator = (Func<X>)CreateY_New},
})
{
var creator = creatorInfo.Creator;
var sum = 0;
for (var i = 0; i < 1000; i++)
sum += creator().Z;
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < iterations; ++i)
{
var x = creator();
sum += x.Z;
}
stopwatch.Stop();
Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
}
}
public class X
{
public X() { }
public X(int z) { this.Z = z; }
public int Z;
}
public class Y : X { }
これはリフレクションとしてマークされていますが、パフォーマンスと複雑さの理由から、リフレクションは最後の手段として一般的に考えられています。あなたのデザインや使用法が反映を必要とする場合がいくつかあります。ただし、検討のためにいくつかの代替案を提示します。
工場を利用するFunc
:
public void SomeMethod(Func<BaseClass> createDerived)
{
BaseClass d = createDerived();
}
メソッドに制約のあるジェネリック型を使用するようにします。
public void SomeMethod<TDerived>() where TDerived : BaseClass, new()
{
TDerived d = new TDerived();
}
カバーの下で、この最後の代替手段は利用しますActivator.CreateInstance
他の人が示唆しているように。どちらもパラメータのないコンストラクタを必要とするため、最後のほうがリフレクションを好みますが、コンパイラは派生型がパラメータのないコンストラクタを持たなければならないという制約を強制しますが、リフレクションアプローチでは実行時例外が発生します。
Func
アプローチは、派生クラスのパラメータ化されたコンストラクタを呼び出すことができるLambda式に対してオープンです。例えば:SomeMethod(() => new MyDerived(DateTime.Now);
- devgeezer
ここでの問題は、あなたがタイプを知ることができないということです。DerivedClass
コンパイル時に。
ただし、このようなことは可能です。
BaseClass obj = new DerivedClass();
これは次のように実装されています。
BaseClass obj = (BaseClass)Activator.CreateInstance(this.GetType());
ただし、DerivedClassにパラメータのないコンストラクタがない場合、この呼び出しは失敗します。
これは本当にあなたが "実行時"とは何を意味するのか、そして何が目的なのかに依存します。たとえば、JonとBasはどちらも、Reflectionを使用して特定のクラスを遅延バインドし、実行時にインスタンス化するという考えを打ち出しました。これは確かに1つのアイデアであり、それが目的であれば、実行時にオブジェクトのメソッドを見つけることさえできます。
インターフェースを使用している場合は、いくつかの追加オプションがあります。 Microsoft Extensibility Framework(またはMEF)は、実行時の発見可能性とインスタンス化が含まれるため、検討したいオプションかもしれません。欠点は、発見されたクラスが正しいインターフェースに従わなければならないことですが、これはほとんどの場合本当の問題ではありません。
あなたがロードしているクラスを知っていて、それらが同じインターフェース(共通のテーマ)を持っているが、実行時に異なるクラスをインスタンス化したいのであれば、IoCコンテナはオプションです。これは特にあなたが求めていることではありません。
動的キーワードはあなたが探しているものではありません。実行時にロードされますが、動的な方法は、呼び出しているメソッドが実際に存在するかどうかをコンパイラがチェックしないことです。誤って実行された場合、存在しないメソッドを呼び出すと、興味深い結果が出る可能性があります。私がダイアナミックについて見た主な動機は、IronPythonのような動的言語との対話です。
public void SomeMethod()
{
object x =Activator.CreateInstance(this.GetType());
}
これで新しいインスタンスが作成されます。一方で、なぜこれをやろうとしているのでしょうか。
確かにあなたが使用する必要がありますがActivator.CreateInstanceあなたは特にに調べてみたいかもしれませんActivator.CreateInstance(String、String)これは実行時に知っているかもしれないクラスの名前を使って呼び出すことができます。
あなたが基本型の上に派生型をインスタンス化しているならば、これは最も有益です。あなたが電話をするつもりならSomeMethod
派生型自体から、それから以前の答えを使ってthis.GetType()
十分なはずです。