12

この質問にはすでに答えがあります。

実行時に決定される型のインスタンスを作成するための(.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キーワードの使用について少し読んだことがありますが、これらについては経験がありません。


7 답변


10

あなたは探しているActivator.CreateInstance(他のオーバーロードもあります。これですそれはコンストラクタ引数を受け取ります)。だからあなたは書くことができます

var anotherOneLikeMe = Activator.CreateInstance(this.GetType());

ここに問題があるかもしれませんanotherOneLikeMe次のように入力されますobjectですから、あなたがそれを共通の基本クラスにキャストするつもりでない限り(例:BaseClassあなたの例では)あなたがそれでできることはあまりありません。


20

実行時にインスタンスを繰り返し作成するパフォーマンスのための最良の方法は、コンパイル式です。

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 { }


  • ここで確認されているように、コンストラクタの呼び出しがアクティベータよりも速いことを見てうれしかったです。blogs.msdn.com/b/haibo_luo/archive/2005/11/17/494009.aspxそしてここbloggingabout.net/blogs/vagif/archive/2010/04/02/… - nawfal
  • 6.6236752呼び出しオプションの最初の6つはタイプミスですか? - Jeff
  • @Jeff、私も「ただ正しいことはできない」と思いました。最適化を有効にしてコードをLinqpadに貼り付けましたが、結果はほぼ一貫していました(3年が経過したと仮定)。 DarkGrayの例と同じ順序で:00:00:00.4287503、00:00:00.4431353、00:00:01.0507991、00:00:00.0809354と00:00:00.0231360 - easuter
  • うーん、私は自分のコードを実行してみる必要がありますが、私自身の実験では、ConstructorInfo.Invokeは、特にActivatorを使用する場合と比べてかなり高速に見えます。 - Jeff
  • うん、そうだね。本当に遅いことを確認したところです。煩わしい - Jeff

4

これはリフレクションとしてマークされていますが、パフォーマンスと複雑さの理由から、リフレクションは最後の手段として一般的に考えられています。あなたのデザインや使用法が反映を必要とする場合がいくつかあります。ただし、検討のためにいくつかの代替案を提示します。

工場を利用する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

2

ここでの問題は、あなたがタイプを知ることができないということです。DerivedClassコンパイル時に。

ただし、このようなことは可能です。

BaseClass obj = new DerivedClass();

これは次のように実装されています。

BaseClass obj = (BaseClass)Activator.CreateInstance(this.GetType());

ただし、DerivedClassにパラメータのないコンストラクタがない場合、この呼び出しは失敗します。


1

これは本当にあなたが "実行時"とは何を意味するのか、そして何が目的なのかに依存します。たとえば、JonとBasはどちらも、Reflectionを使用して特定のクラスを遅延バインドし、実行時にインスタンス化するという考えを打ち出しました。これは確かに1つのアイデアであり、それが目的であれば、実行時にオブジェクトのメソッドを見つけることさえできます。

インターフェースを使用している場合は、いくつかの追加オプションがあります。 Microsoft Extensibility Framework(またはMEF)は、実行時の発見可能性とインスタンス化が含まれるため、検討したいオプションかもしれません。欠点は、発見されたクラスが正しいインターフェースに従わなければならないことですが、これはほとんどの場合本当の問題ではありません。

あなたがロードしているクラスを知っていて、それらが同じインターフェース(共通のテーマ)を持っているが、実行時に異なるクラスをインスタンス化したいのであれば、IoCコンテナはオプションです。これは特にあなたが求めていることではありません。

動的キーワードはあなたが探しているものではありません。実行時にロードされますが、動的な方法は、呼び出しているメソッドが実際に存在するかどうかをコンパイラがチェックしないことです。誤って実行された場合、存在しないメソッドを呼び出すと、興味深い結果が出る可能性があります。私がダイアナミックについて見た主な動機は、IronPythonのような動的言語との対話です。


0

public void SomeMethod()
{
    object x =Activator.CreateInstance(this.GetType());
}

これで新しいインスタンスが作成されます。一方で、なぜこれをやろうとしているのでしょうか。


0

確かにあなたが使用する必要がありますがActivator.CreateInstanceあなたは特にに調べてみたいかもしれませんActivator.CreateInstance(String、String)これは実行時に知っているかもしれないクラスの名前を使って呼び出すことができます。

あなたが基本型の上に派生型をインスタンス化しているならば、これは最も有益です。あなたが電話をするつもりならSomeMethod派生型自体から、それから以前の答えを使ってthis.GetType()十分なはずです。

リンクされた質問


関連する質問

最近の質問