604

사람은 항상 그 사실을 알지 못할 수도 있습니다.Type컴파일 타임에 객체의 인스턴스를 만들지 만,Type. 어떻게 새로운 객체 인스턴스를 얻을 수 있습니까?Type?

12 답변


759

그만큼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");


  • 두 번째 호출이 마침내 찾았 기 때문에 다행 스럽지만 따옴표가 빠졌고 parms가 반대로되어 있어야합니다. ObjectType instance = (ObjectType) Activator.CreateInstance ( "MyAssembly", "MyNamespace.ObjectType"); - kevinc
  • 당신은 ' 언 래핑 () ' 원하는 실제 유형의 객체를 얻으려면 : ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap (); - Ε Г И І И О
  • 어떻게 하죠?ObjectType instanceOP의 조건과 일치합니다. "컴파일 타임에 항상 객체의 유형을 알 수는 없습니다." ? :피 - MA-Maddin
  • @ MA-Maddin 좋아요,object instance = Activator.CreateInstance(...);. - BrainSlugs83

119

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

그만큼Activator클래스에는 조금 더 쉽게 만들 수있는 일반적인 변형이 있습니다.

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


  • 단, 런타임 용으로는 작동하지 않습니다.Type t. - Kevin P. Rice
  • @ 케빈 물론. 이러한 작업캔트의미가 없기 때문에 정적으로 입력 된 언어로 작업하십시오. 알 수없는 유형의 오브젝트에서는 메소드를 호출 할 수 없습니다. 그동안 (이 답변을 쓰는 이래로) C #은dynamic어느 것~하다그런 구조를 허용하지만 대부분의 목적을 위해이 대답은 여전히 그것을 다루고있다. - Konrad Rudolph
  • @ KonradRudolph 사실이 아닙니다. 첫 번째 C #~하다런타임에 새로운 유형을 만들 수 있습니다. 아무 것도 호출 할 수 없습니다.정적으로 안전한 방법으로. 그래서 네, 맞습니다. 하지만 실제로 런타임에 어셈블리를로드 할 때이 옵션이 필요합니다. 즉, 컴파일 타임에 형식을 알 수 없습니다. C #은 당신이 할 수 없다면 심각하게 제한 될 것입니다. 당신이 직접 증명 한 것입니다 : Type-Instance 작업을 수행하는 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, 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

전체 코드 :

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/1242) TypeDescriptor.AddProvider와 함께 사용하면 더 빠를 수 있습니다. - Lars Truijens
  • 어떤 유형을 모르는 경우에도 여전히 유용합니까?X런타임에? - ajeh
  • @ 예 예. typeof (T)를 Type.GetType (..)으로 변경하십시오. - Serj-Tm
  • @ Serj-Tm 아니요, 유형 X가 런타임 인 경우 작동하지 않습니다.Type. - NetMage

39

이 문제의 한 가지 구현은 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");


  • 두 번째 경우 (외부 어셈블리)에는 " Vehicles.Car, OtherAssembly " 귀하의 첫 번째 방법에 그것은 작동합니다. 분명히 OtherAssembly는 그것이 살고있는 어셈블리의 이름입니다. - danmiser
  • @danmiser 어셈블리 이름을 하드 코딩해야합니다. 유연성을 구현하기 위해 null을 확인하고 코드가 동적 인 방식으로 작동합니다. :) - Sarath Avanavu

12

이것이 애플리케이션 인스턴스에서 많이 호출 될 것이라면 액티베이터를 사용하는 대신 동적 코드를 컴파일하고 캐시하는 것이 훨씬 빠릅니다.ConstructorInfo.Invoke(). 동적 컴파일을위한 두 가지 쉬운 옵션이 컴파일됩니다.Linq 표현또는 몇 가지 간단한ILopcode 및DynamicMethod. 어느 쪽이든, 당신은 꽉 루프 또는 여러 통화에 들어가기 시작하면 큰 차이가 있습니다.


  • "IL opcode 및 DynamicMethod" 링크가 죽었습니다. - Judge Bread

9

리플렉션을 사용하지 않고 :

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


  • 어떻게 유용할까요? 이 메서드를 호출하기 위해 이미 형식을 알아야하며 형식을 알고 있으면 특별한 방법없이이 형식을 생성 할 수 있습니다. - Kyle Delaney
  • 따라서 T는 런타임에 달라질 수 있습니다. 당신이 Derived Type으로 작업 할 때 유용합니다. - user887983
  • 새로운 T (); T가 매개 변수없는 생성자가있는 참조 유형이 아닌 경우 실패합니다.이 메소드는 T가 참조 유형이고 생성자가 있음을 보장하기 위해 제약 조건을 사용합니다. - user887983
  • T는 런타임에 어떻게 달라질 수 있습니까? Create < >를 호출하려면 디자인 타임에 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();작업?


  • 실제로, 그것은 일반적인 클래스 / 메소드에서 있지만, 주어진 "유형"에 대해서는 그렇지 않을 것이다. - Brady Moritz
  • 유형 T에 ' 새 () ' 강제. - 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

연결된 질문


최근 질문