604

常に知っているとは限りません。Typeオブジェクトはコンパイル時に生成されますが、インスタンスを作成する必要があるかもしれません。Type。どのようにしてから新しいオブジェクトインスタンスを取得しますかType

12 답변


759

Activatorルート内のクラスSystem名前空間はかなり強力です。

コンストラクタなどにパラメータを渡すためのたくさんのオーバーロードがあります。次のURLにある資料を調べてください。

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

または(新しいパス)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

ここにいくつかの簡単な例があります:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");


  • ObjectType instance =(ObjectType)Activator.CreateInstance(" MyAssembly"、" MyNamespace.ObjectType");最後にこれを見つけたことを嬉しく思いますが、2回目の呼び出しは正確ではなく、引用符とパラメータを逆にする必要があります。 - kevinc
  • Unwrap()を呼び出す必要があります。必要なオブジェクトの実際の型を取得するには、ConcreteType instance =(ConcreteType)Activator.CreateInstance(objectType).Unwrap();を使用します。 - Ε Г И І И О
  • どうやってObjectType instanceOPの条件に一致します。「コンパイル時にオブジェクトの種類を常に把握しているとは限りません」。 ? :P - MA-Maddin
  • @ MA-マディンそれでは、object instance = Activator.CreateInstance(...);。 - BrainSlugs83

119

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

Activatorclassにはこれを少し簡単にする一般的な変形があります。

ObjectType instance = Activator.CreateInstance<ObjectType>();


  • これを除いてランタイムには機能しませんType t。 - Kevin P. Rice
  • @ケビンもちろんです。そのような操作できない意味がないため、静的型付け言語で作業してください。未知の型のオブジェクトに対してメソッドを呼び出すことはできません。それまでの間(=この回答を書いてから)、C#はdynamic構築するするそのような構成を許可しますが、ほとんどの目的のためにこの答えはまだそれをカバーしています。 - Konrad Rudolph
  • @ KonradRudolph全く違います。最初のC#する実行時に新しい型を作成できます。あなたは彼らに何かを呼び出すことはできません静的に安全な方法で。だから、あなたは半分正しいです。しかし、もっと現実的には、実行時にアセンブリをロードするときにこれが必要になります。つまり、コンパイル時に型がわからなくなります。これができない場合、C#は大幅に制限されます。私はあなたがそれを自分自身で証明したことを意味します。型インスタンスを受け取るActivatorメソッドは他にどのように機能するのでしょうか。 MSがActivatorクラスを書いたとき、彼らはユーザーが書く将来の型についてコンパイル時の知識を持っていませんでした。 - AnorZaken
  • @AnorZaken私のコメントは実行時に型を作成することについては何も言いません。もちろんそれは可能ですが、使うことはできません静的に同じ文脈で(もちろん、完全に静的にコンパイルされたプログラムをホストすることもできます)。私のコメントはこれだけです。 - Konrad Rudolph
  • @ KonradRudolph申し訳ありませんが、「このような操作」を誤って解釈しました。実行時にのみ認識される型をインスタンス化することを意味します。ジェネリック型パラメータとしてランタイム型を使用することを意味する代わりに。 - AnorZaken

93

コンパイルされた表現は最善の方法です! (実行時に繰り返しインスタンスを作成するパフォーマンス用)

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

統計(2017年、LINQPad 5.22.02 / x 64 / .NET 4.6):

    Iterations: 5000000
    No args
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName)
    00:00:00.3500748, Activator.CreateInstance(Type type)
    00:00:01.0100714, ConstructorInfo.Invoke
    00:00:00.1375767, Compiled expression
    00:00:00.1337920, Compiled expression (type)
    00:00:00.0593664, new
    Single arg
    00:00:03.9300630, Activator.CreateInstance(Type type)
    00:00:01.3881770, ConstructorInfo.Invoke
    00:00:00.1425534, Compiled expression
    00:00:00.0717409, new

フルコード

static X CreateY_New()
{
    return new Y();
}

static X CreateY_New_Arg(int z)
{
    return new Y(z);
}

static X CreateY_CreateInstance()
{
    return (X)Activator.CreateInstance(typeof(Y));
}

static X CreateY_CreateInstance_String()
{
    return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}

static X CreateY_CreateInstance_Arg(int z)
{
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, });
}

private static readonly System.Reflection.ConstructorInfo YConstructor =
    typeof(Y).GetConstructor(Type.EmptyTypes);
private static readonly object[] Empty = new object[] { };
static X CreateY_Invoke()
{
    return (X)YConstructor.Invoke(Empty);
}

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg =
    typeof(Y).GetConstructor(new[] { typeof(int), });
static X CreateY_Invoke_Arg(int z)
{
    return (X)YConstructor_Arg.Invoke(new object[] { z, });
}

private static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
static X CreateY_CompiledExpression()
{
    return YCreator();
}

private static readonly Func<X> YCreator_Type = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y))
).Compile();
static X CreateY_CompiledExpression_Type()
{
    return YCreator_Type();
}

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z");
private static readonly Func<int, X> YCreator_Arg = Expression.Lambda<Func<int, X>>(
   Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }),
   YCreator_Arg_Param
).Compile();
static X CreateY_CompiledExpression_Arg(int z)
{
    return YCreator_Arg(z);
}

static void Main(string[] args)
{
    const int iterations = 5000000;

    Console.WriteLine("Iterations: {0}", iterations);

    Console.WriteLine("No args");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "Activator.CreateInstance(Type 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 = "Compiled expression (type)", Creator = (Func<X>)CreateY_CompiledExpression_Type},
        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);
    }

    Console.WriteLine("Single arg");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<int, X>)CreateY_CreateInstance_Arg},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<int, X>)CreateY_Invoke_Arg},
        new {Name = "Compiled expression", Creator = (Func<int, X>)CreateY_CompiledExpression_Arg},
        new {Name = "new", Creator = (Func<int, X>)CreateY_New_Arg},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator(i).Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator(i);
            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
{
    public Y() {}
    public Y(int z) : base(z) {}
}


  • すべての統計について+1!現時点ではこの種のパフォーマンスは実際には必要ありませんが、それでも非常に興味深いものです。 :) - AnorZaken
  • TypeDescriptor.CreateInstanceもあります(stackoverflow.com/a/17797389/1242TypeDescriptor.AddProviderと一緒に使用すると、より高速になります。 - Lars Truijens
  • あなたはどんなタイプがわからないときにこれはまだ便利ですかX実行時ですか? - ajeh
  • @ ajehはい。 typeof(T)をType.GetType(..)に変更します。 - Serj-Tm
  • @ Serj-Tmいいえ、タイプXがランタイムの場合は動作しませんType。 - NetMage

39

この問題の1つの実装は、Typeのパラメータのないコンストラクタを呼び出そうとすることです。

public static object GetNewObject(Type t)
{
    try
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return null;
    }
}

これは一般的な方法に含まれる同じアプローチです。

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}


  • 例外駆動型プログラミング単純に型を考慮してコンストラクタを決定することができる場合、これは非常に貧弱な実装のように見えます。 - Firoso

12

それはとても簡単です。あなたのクラス名はCarそして名前空間はVehiclesその後、次のようにパラメータを渡します。Vehicles.Car型のオブジェクトを返すCar。このように、あらゆるクラスのあらゆるインスタンスを動的に作成することができます。

public object GetInstance(string strNamesapace)
{         
     Type t = Type.GetType(strNamesapace); 
     return  Activator.CreateInstance(t);         
}

もしあなたの完全修飾名(すなわち、Vehicles.Carこの場合)別のアセンブリにあります、Type.GetTypenullになります。このような場合は、すべてのアセンブリをループ処理してType。そのためには、以下のコードを使用することができます

public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

そして上記のメソッドを呼び出すことでインスタンスを取得できます。

object objClassInstance = GetInstance("Vehicles.Car");


  • 2番目のケース(外部アセンブリ)では、" Vehicles.Car、OtherAssembly"を渡すだけです。あなたの最初の方法にそれはうまくいくでしょう。明らかにOtherAssemblyはそれが存在するアセンブリの名前です。 - danmiser
  • @danmiserこれはアセンブリ名をハードコーディングする必要があります。柔軟性を実現するために、nullをチェックしています。コードは動的に機能します。 - Sarath Avanavu

12

これがアプリケーションインスタンス内で頻繁に呼び出されることになる場合は、アクティベータを使用する代わりに動的コードをコンパイルしてキャッシュするほうがはるかに高速です。ConstructorInfo.Invoke()。動的コンパイルのための2つの簡単なオプションがコンパイルされていますリンク式またはいくつかの簡単なILオペコードとDynamicMethod。いずれにせよ、あなたがタイトなループや複数の呼び出しに入り始めるときに違いは大きいです。


  • " ILオペコードとDynamicMethod"リンクが切れています。 - Judge Bread

9

リフレクションを使用しないで:

private T Create<T>() where T : class, new()
{
    return new T();
}


  • これはどのように便利ですか?そのメソッドを呼び出すには、その型を知っている必要があります。型がわかっている場合は、特別なメソッドを使用せずにそれを構築できます。 - Kyle Delaney
  • したがって、Tは実行時に変わる可能性があります。既定の型で作業する場合に便利です。 - user887983
  • 新しいT() Tがパラメータのないコンストラクタを持つ参照型でない場合、このメソッドはTを参照型にしてコンストラクタを持つように制約を使用します。 - user887983
  • 実行時にTはどのように変わりますか?作成<>を呼び出すためにデザイン時にTを知っている必要はありませんか。 - Kyle Delaney
  • ファクトリ内でジェネリッククラスとインタフェースを扱う場合は、そのインタフェースを実装する型を実体化する必要があります。 - user887983

7

デフォルトのコンストラクタを使いたいのであれば、System.Activator以前に提示されたものはおそらく最も便利です。ただし、型にデフォルトのコンストラクタがない場合、またはデフォルト以外のコンストラクタを使用する必要がある場合は、リフレクションを使用するか、リフレクションを使用するかを選択できます。System.ComponentModel.TypeDescriptor。リフレクションの場合、型名(名前空間と共に)だけを知っていれば十分です。

リフレクションを使用した例

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
        typeName: objectType.FulName, // string including namespace of the type
        ignoreCase: false,
        bindingAttr: BindingFlags.Default,
        binder: null,  // use default binder
        args: new object[] { args, to, constructor },
        culture: null, // use CultureInfo from current thread
        activationAttributes: null
    );

使用例TypeDescriptor

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
        provider: null, // use standard type description provider, which uses reflection
        objectType: objectType,
        argTypes: new Type[] { types, of, args },
        args: new object[] { args, to, constructor }
    );


7

一般的ではないでしょうかT t = new T();作業?


  • 実際には、それは一般的なクラス/メソッドになりますが、特定の" Type"に対してはそうではありません。 - Brady Moritz
  • 型Tにはnew()があるとします。制約 - Rob Von Nesselrode

5

この問題を考えると、Activatorはパラメータのないctorがあるときに動作します。これが制約の場合は、

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()


3

public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}


3

私は(デフォルトのコンストラクタを使って)任意のクラスのための単純なCloneObjectメソッドを実装しようとしていたので、私はこの質問に遭遇することができます

一般的な方法では、型がNew()を実装することを要求できます。

Public Function CloneObject(Of T As New)(ByVal src As T) As T
    Dim result As T = Nothing
    Dim cloneable = TryCast(src, ICloneable)
    If cloneable IsNot Nothing Then
        result = cloneable.Clone()
    Else
        result = New T
        CopySimpleProperties(src, result, Nothing, "clone")
    End If
    Return result
End Function

非ジェネリックと仮定すると、型はデフォルトのコンストラクタとcatchを持ちます。 そうでない場合は例外です。

Public Function CloneObject(ByVal src As Object) As Object
    Dim result As Object = Nothing
    Dim cloneable As ICloneable
    Try
        cloneable = TryCast(src, ICloneable)
        If cloneable IsNot Nothing Then
            result = cloneable.Clone()
        Else
            result = Activator.CreateInstance(src.GetType())
            CopySimpleProperties(src, result, Nothing, "clone")
        End If
    Catch ex As Exception
        Trace.WriteLine("!!! CloneObject(): " & ex.Message)
    End Try
    Return result
End Function

リンクされた質問


最近の質問