리플렉션을 사용하여 C #에서 기본 생성자없이 형식의 인스턴스 만들기


97

다음 클래스를 예로 들어 보겠습니다.

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

그런 다음 리플렉션을 사용하여이 유형의 인스턴스를 만들고 싶습니다.

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

일반적으로 이것은 작동하지만 SomeType매개 변수없는 생성자를 정의 하지 않았기 때문에 를 호출하면 " 이 개체에 대해 매개 변수없는 생성자가 정의되지 않았습니다. "라는 메시지와 함께 Activator.CreateInstance유형의 예외가 발생 합니다. 이 유형의 인스턴스를 만드는 다른 방법이 있습니까? 내 모든 클래스에 매개 변수없는 생성자를 추가하는 것은 다소 짜증날 것입니다.MissingMethodException


2
FormatterServices.GetUninitializedObject초기화되지 않은 문자열을 만들 수 없습니다. 예외가 발생할 수 있습니다 System.ArgumentException: Uninitialized Strings cannot be created..이 점을 명심하십시오.
— Bartosz Pierzchlewicz

주의 해 주셔서 감사합니다.하지만 저는 이미 문자열과 기본 유형을 별도로 처리하고 있습니다.
— Aistina

답변:


143

나는 원래이 답변을 여기에 게시 했지만 이것은 정확히 동일한 질문이 아니지만 동일한 답변을 가지고 있기 때문에 여기에 재판이 있습니다.

FormatterServices.GetUninitializedObject()생성자를 호출하지 않고 인스턴스를 생성합니다. Reflector 를 사용 하고 일부 핵심 .Net 직렬화 클래스 를 파헤쳐이 클래스를 찾았습니다 .

아래 샘플 코드를 사용하여 테스트했는데 잘 작동하는 것 같습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
            myClass.One = 1;
            Console.WriteLine(myClass.One); //write "1"
            Console.ReadKey();
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
        }

        public int One
        {
            get;
            set;
        }
    }
}

대단해, 그게 바로 내가 필요한 것 같아. 초기화되지 않은 것은 모든 메모리가 0으로 설정된다는 것을 의미한다고 가정합니까? (구조체가 인스턴스화되는 방법과 유사)
— Aistina

각 유형에 대한 기본값이 기본값이됩니다. 따라서 객체는 null, ints 0 등이됩니다. 클래스 수준 초기화가 발생한다고 생각하지만 생성자는 실행되지 않습니다.
— Jason Jackson

14
@JSBangs, 완벽하게 합법적 인 대답을 딩딩하는 것이 짜증납니다. 귀하의 의견과 다른 답변은 실제로 질문을 처리하지 않습니다. 더 나은 답이 있다고 생각되면 답을 제공하십시오. 그러나 내가 제공 한 대답은 다른 직렬화 클래스가이 코드를 사용하는 것과 같은 방식으로 문서화 된 클래스를 사용하는 방법을 강조합니다.
— Jason Jackson

21
@JSBangs FormatterServices ( msdn.microsoft.com/en-us/library/… )는 문서화되지 않았습니다.
— Autodidact


72

CreateInstance 메서드의이 오버로드를 사용합니다.

public static Object CreateInstance(
    Type type,
    params Object[] args
)

지정된 매개 변수와 가장 일치하는 생성자를 사용하여 지정된 형식의 인스턴스를 만듭니다.

참조 : http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx


1
이 솔루션은 문제를 지나치게 단순화합니다. 내 유형을 모르고 "이 유형 변수에 유형의 객체를 생성하십시오"라고 말하면 어떻게됩니까?
— kamii

23

내가 벤치마킹 했을 때 성능 (T)FormatterServices.GetUninitializedObject(typeof(T))이 더 느 렸습니다. 동시에 컴파일 된 표현식은 기본 생성자가있는 유형에 대해서만 작동하지만 속도가 크게 향상됩니다. 나는 하이브리드 접근 방식을 취했습니다.

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

즉, create 표현식이 효과적으로 캐시되고 유형이 처음로드 될 때만 페널티가 발생합니다. 값 유형도 효율적인 방식으로 처리합니다.

불러라:

MyType me = New<MyType>.Instance();

참고 (T)FormatterServices.GetUninitializedObject(t)문자열을 실패합니다. 따라서 빈 문자열을 반환하기 위해 문자열에 대한 특수 처리가 적용됩니다.


1
누군가의 코드 한 줄을 보면 하루를 절약 할 수 있다는 것은 이상합니다. 감사합니다! 성능상의 이유로 나를 귀하의 게시물로 데려가 트릭이 완료되었습니다. :) FormatterServices 및 Activator 클래스는 컴파일 된 표현식에 비해 성능이 저조합니다.
— jmodrak

@nawfal 문자열에 대한 특수 처리와 관련하여,이 특수 처리없이 문자열에 대해 실패 할 것이라는 것을 알고 있지만 알고 싶습니다. 다른 모든 유형 에서도 작동 합니까?
— Sнаđошƒаӽ jul.

@ Sнаđошƒаӽ 안타깝게도 아니요. 주어진 예는 베어 본이며 .NET에는 다양한 유형의 유형이 있습니다. 예를 들어 대리자 유형을 전달하는 경우 인스턴스를 어떻게 제공합니까? 아니면 생성자가 던지면 어떻게 할 수 있습니까? 그것을 처리하는 다양한 방법. 나는 내 라이브러리에서 더 많은 시나리오를 처리하기 위해이 업데이트에 응답했습니다. 지금은 어디에도 게시되지 않았습니다.
— nawfal

4

좋은 답변이지만 dot net compact 프레임 워크에서는 사용할 수 없습니다. 다음은 CF.Net에서 작동하는 솔루션입니다.

class Test
{
    int _myInt;

    public Test(int myInt)
    {
        _myInt = myInt;
    }

    public override string ToString()
    {
        return "My int = " + _myInt.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
        var obj = ctor.Invoke(new object[] { 10 });
        Console.WriteLine(obj);
    }
}

1
이것이 기본이 아닌 생성자를 호출하는 방법입니다. 생성자를 전혀 호출하지 않고 객체를 만들고 싶을 지 모르겠습니다.
— Rory MacLeod

2
사용자 지정 serializer를 작성하는 경우 생성자를 호출하지 않고 개체를 만들 수 있습니다.
— Autodidact

1
네,이 질문에 대한 정확한 사용 사례 시나리오입니다. :)
— Aistina

1
@Aistina 아마도이 정보를 질문에 추가 할 수 있습니까? 대부분의 사람들은 자신의 행위자를 부르지 않고 객체를 만드는 것에 반대하고 시간을내어 그것에 대해 논쟁 할 것입니다. 그러나 귀하의 사용 사례는 실제로 그것을 정당화하므로 질문 자체와 매우 관련이 있다고 생각합니다.
— julealgon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.