유형에서 새 객체 인스턴스를 만드는 방법


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

20
마지막으로 이것을 발견하게되어 기쁘지만, 두 번째 호출이 옳지 않다. 인용 부호와 매개 변수가 빠졌다면, ObjectType instance = (ObjectType) Activator.CreateInstance ( "MyAssembly", "MyNamespace.ObjectType");
kevinc 2016 년

9
원하는 실제 객체 유형을 얻으려면 'Unwrap ()'을 호출해야합니다. ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
Ε Г И І И О

4
ObjectType instanceOP의 조건 "컴파일 타임에 객체의 유형을 항상 알 수있는 것은 아닙니다"와 어떻게 일치합니까? : P
마틴 슈나이더

@ MA-Maddin 그렇다면, object instance = Activator.CreateInstance(...);.
BrainSlugs83

1
누구나 .NET Core 에서이 작업을 수행하는 방법을 알고 있습니까? Unwrap 메소드는 오브젝트에서 사용할 수 없습니다.
Justin

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

Activator클래스에는 이것을 좀 더 쉽게 만드는 일반적인 변형이 있습니다.

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

8
@ 케빈 물론입니다. 이러한 작업 의미가 없기 때문에 정적으로 형식이 지정된 언어로는 작동하지 않습니다. 알 수없는 유형의 객체에서는 메소드를 호출 할 수 없습니다. 한편 (=이 답변 쓰기 때문에) C 번호를 가지고 dynamic구조 않는 등 구조 그러나 대부분의 목적이 대답은 여전히 그것을 커버를 할 수 있습니다.
Konrad Rudolph

1
@KonradRudolph 사실이 아닙니다. C #을 우선하는 것은 않습니다 런타임에 새로운 유형을 만들 수 있습니다. 정적으로 안전한 방법으로는 아무 것도 호출 할 수 없습니다 . 네, 반은 맞습니다. 그러나 더 현실적으로는 런타임에 어셈블리를로드 할 때 이것이 필요합니다. 즉 컴파일시 유형을 알 수 없습니다. 이 작업을 수행 할 수 없으면 C #이 심각하게 제한됩니다. 방금 직접 증명 했습니까? 유형 인스턴스를 취하는 Activator 방법은 어떻게 다른가요? MS가 Activator 클래스를 작성할 때 사용자가 작성할 미래 유형에 대한 컴파일 타임 지식이 없었습니다.
AnorZaken

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

1
또한 TypeDescriptor.CreateInstance (참조가 stackoverflow.com/a/17797389/1242를 빠르게 TypeDescriptor.AddProvider 경우에 사용될 수있다)
라스 Truijens

2
X런타임에 어떤 유형인지 모르는 경우에도 여전히 유용 합니까?
ajeh

1
@ajeh 예. typeof (T)를 Type.GetType (..)으로 변경하십시오.
Serj-Tm

3
@ Serj-Tm 아니요, X 유형이 런타임이면 작동하지 않습니다 Type.
NetMage

47

이 문제의 한 가지 구현은 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
예외 구동 프로그래밍? 생성자를 결정하기 위해 단순히 유형을 반영 할 수있을 때 구현이 매우 좋지 않은 것 같습니다.
Firoso

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

두 번째 경우 (외부 어셈블리), "Vehicles.Car, OtherAssembly"를 첫 번째 방법으로 전달하면 작동합니다. 분명히 OtherAssembly는 그것이 살고있는 집회의 이름입니다.
danmiser

2
@danmiser 어셈블리 이름을 하드 코딩해야합니다. 유연성을 구현하기 위해 null을 확인하고 코드는 동적으로 작동합니다 :)
Sarath Avanavu

14

이것이 응용 프로그램 인스턴스에서 많이 호출되는 것이라면 활성제 또는를 사용하는 대신 동적 코드를 컴파일하고 캐시하는 것이 훨씬 빠릅니다 ConstructorInfo.Invoke(). 동적 컴파일을위한 두 가지 쉬운 옵션은 Linq Expressions 또는 일부 간단한 ILopcode 및DynamicMethod 입니다. 어느 쪽이든 타이트한 루프 나 여러 호출을 시작할 때 차이가 큽니다.



10

기본 생성자를 사용하려면 System.Activator앞에서 제시 한 솔루션 이 가장 편리합니다. 그러나 유형에 기본 생성자가 없거나 기본이 아닌 유형을 사용해야하는 경우 reflection 또는을 사용하는 옵션이 있습니다 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
이것이 어떻게 유용합니까? 해당 메소드를 호출하려면 이미 유형을 알고 있어야하며, 유형을 알고있는 경우 특수 메소드없이 유형을 구성 할 수 있습니다.
Kyle Delaney 2012

따라서 T는 런타임에 달라질 수 있습니다. 파생 유형으로 작업하는 경우에 유용합니다.

새로운 T (); T가 매개 변수가없는 생성자를 가진 참조 유형이 아닌 경우 실패합니다.이 메서드는 제약 조건을 사용하여 T가 참조 유형이고 생성자가 있는지 확인합니다.

3
런타임에 T는 어떻게 달라질 수 있습니까? Create <>를 호출하기 위해 디자인 타임에 T를 알 필요가 없습니까?
Kyle Delaney 2012

팩토리에서 일반 클래스 및 인터페이스를 사용하는 경우 인터페이스를 구현하는 유형이 다를 수 있습니다.

8

이 문제가 발생하면 매개 변수가없는 ctor가있을 때 Activator가 작동합니다. 이것이 제약 조건이라면

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.