タイプから新しいオブジェクトインスタンスを作成する方法


748

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

どのようにして新しいオブジェクトインスタンスを取得しますTypeか?

回答:


896

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

コンストラクターなどにパラメーターを渡すためのオーバーロードはたくさんあります。次の場所にあるドキュメントを確認してください。

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");

21
最後にこれを見つけてうれしいが、2回目の呼び出しは正確ではなく、引用符がなく、parmsが逆になっているはずです。ObjectTypeinstance =(ObjectType)Activator.CreateInstance( "MyAssembly"、 "MyNamespace.ObjectType");
kevinc 2013年

10
必要なオブジェクトの実際のタイプを取得するには、 'Unwrap()'を呼び出す必要があります。ConcreteType instance =(ConcreteType)Activator.CreateInstance(objectType).Unwrap();
ΕГИІИО

4
ObjectType instanceOPの条件「コンパイル時にオブジェクトのタイプが常にわかるとは限らない」はどのように一致しますか?:P
マーティンシュナイダー

@ MA-Maddin大丈夫object instance = Activator.CreateInstance(...);です。
BrainSlugs83 2018年

1
誰でもこれを.NET Coreで行う方法を知っていますか?Unwrapメソッドはオブジェクトでは使用できません。
ジャスティン

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

このActivatorクラスには、これを少し簡単にする一般的なバリアントがあります。

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

8
@Kevinもちろんです。このような操作意味をなさないため、静的型付け言語では機能しません。不明なタイプのオブジェクトに対してメソッドを呼び出すことはできません。一方で(=この回答を書いているので)C#を持っているdynamic構文ないような構築物を許可しますが、ほとんどの目的のために、この答えはまだそれをカバーしています。
Konrad Rudolph

1
@KonradRudolph真実ではありません。最初のc#では、実行時に新しい型を作成できます。静的に安全な方法でそれら呼び出すことはできません。ええ、あなたは半分正解です。ただし、より現実的には、実行時にアセンブリを読み込むときにこれが必要になります。つまり、コンパイル時に型が不明です。これを実行できない場合、C#は大幅に制限されます。私はあなたがそれを自分で証明したことを意味します:型インスタンスをとるActivatorメソッドは他にどのように機能しますか?MSがActivatorクラスを作成したとき、ユーザーが作成する将来の型についてコンパイル時の知識がありませんでした。
AnorZaken 2017年

1
@AnorZaken私のコメントは、実行時に型を作成することについて何も述べていません。もちろんそれは可能ですが、同じコンテキストで静的に使用することはできません(もちろん、完全に静的にコンパイルされたプログラムをホストすることはできます)。それは私のコメントが言っているすべてです。
Konrad Rudolph

@KonradRudolph申し訳ありませんが、 "そのような操作"は、実行時にしか認識されない型をインスタンス化することを誤って解釈しています。ランタイム型をジェネリック型パラメーターとして使用する意味ではありません。
AnorZaken

1
@AnorZaken-技術的には、実行時に新しい型を作成し、新しい型が既知のインターフェースを実装するか、既知の基本クラスを継承する場合は、静的に安全な方法でそれらのメソッドを呼び出すことができます。-これらのいずれの方法でも、ランタイムで作成されたオブジェクトの静的コントラクトが得られます。
BrainSlugs83

132

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

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、x64):

    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 / x64 / .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

統計(2019、x64 / .NET 4.8):

Iterations: 5000000
No args
00:00:00.3287835, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.3122015, Activator.CreateInstance(Type type)
00:00:00.8035712, ConstructorInfo.Invoke
00:00:00.0692854, Compiled expression
00:00:00.0662223, Compiled expression (type)
00:00:00.0337862, new
Single arg
00:00:03.8081959, Activator.CreateInstance(Type type)
00:00:01.2507642, ConstructorInfo.Invoke
00:00:00.0671756, Compiled expression
00:00:00.0301489, new

統計(2019、x64 / .NET Core 3.0):

Iterations: 5000000
No args
00:00:00.3226895, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.2786803, Activator.CreateInstance(Type type)
00:00:00.6183554, ConstructorInfo.Invoke
00:00:00.0483217, Compiled expression
00:00:00.0485119, Compiled expression (type)
00:00:00.0434534, new
Single arg
00:00:03.4389401, Activator.CreateInstance(Type type)
00:00:01.0803609, ConstructorInfo.Invoke
00:00:00.0554756, Compiled expression
00:00:00.0462232, 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) {}
}

18
すべての統計の+1!現時点では、このようなパフォーマンスは必要ありませんが、それでも非常に興味深いです。:)
AnorZaken 2016

1
TypeDescriptor.AddProvider
Lars Truijens

2
X実行時にタイプがわからない場合でも、これは役に立ちますか?
ajeh 2017年

1
@ajehはい。typeof(T)をType.GetType(..)に変更します。
Serj-Tm 2017

3
@ Serj-Tmいいえ、タイプXがランタイムの場合は機能しませんType
NetMage 2017

47

この問題の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);
    }
}

15
例外駆動プログラミング?タイプを単純に反映してコンストラクタを決定できる場合、これは非常に貧弱な実装のようです。
フィロソ2014年

16

とてもシンプルです。クラス名が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

2
@danmiserアセンブリ名をハードコーディングする必要があります。柔軟性を実装するために、私はnullをチェックしており、コードは動的に機能します:)
Sarath Avanavu

14

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



10

デフォルトのコンストラクターを使用したい場合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 }
    );

args[]私がこの質問に見つけたものでした、ありがとう!
チャド

10

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

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

5
これはどのように役立ちますか?そのメソッドを呼び出すには、型を知っている必要があります。型がわかっている場合は、特別なメソッドなしで構築できます。
カイルデラニー、

したがって、Tは実行時に変化する可能性があります。派生型を扱う場合に役立ちます。

新しいT(); Tがパラメーターなしのコンストラクターを持つ参照型でない場合は失敗します。このメソッドは制約を使用して、Tが参照型であり、コンストラクターを持っていることを確認します。

3
Tは実行時にどのように変化しますか?Create <>を呼び出すために、設計時にTを知っている必要はありませんか?
カイルデラニー

ファクトリでジェネリッククラスとインターフェースを使用する場合、インターフェースを実装する型はインスタンス化する必要がある場合があります。

8

この問題を考えると、Activatorはパラメーターのないctorがある場合に機能します。これが制約である場合は、使用を検討してください

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()


4

私は任意のクラスのための単純な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

非ジェネリックの場合、型にはデフォルトのコンストラクターがあり、ない場合は例外をキャッチします。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.