리플렉션을 사용하여 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.