web-dev-qa-db-ja.com

リフレクションを使用してC#でデフォルトコンストラクターなしで型のインスタンスを作成する

例として次のクラスを取り上げます。

class Sometype
{
    int someValue;

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

次に、リフレクションを使用してこのタイプのインスタンスを作成します。

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

通常、これは機能しますが、SomeTypeはパラメーターなしのコンストラクターを定義していないため、Activator.CreateInstanceMissingMethodException型の例外をスローし、メッセージ「このオブジェクトにパラメーターレスコンストラクターが定義されていません。」を使用して、この型のインスタンスを作成する別の方法はありますか?すべてのクラスにパラメーターなしのコンストラクターを追加するのはちょっと面倒です。

90
Aistina

私はもともとこの答えを投稿しました here ですが、これはまったく同じ質問ではなく同じ答えがあるので再版です:

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;
        }
    }
}
130
Jason Jackson

CreateInstanceメソッドのこのオーバーロードを使用します。

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

指定されたパラメーターに最も一致するコンストラクターを使用して、指定されたタイプのインスタンスを作成します。

参照: http://msdn.Microsoft.com/en-us/library/wcxyzt4d.aspx

71
Nick

私が ベンチマーク(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;
}

これは、作成式が効果的にキャッシュされ、タイプが最初にロードされたときにのみペナルティが発生することを意味します。値型も効率的に処理します。

あれを呼べ:

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

_(T)FormatterServices.GetUninitializedObject(t)は文字列に対して失敗することに注意してください。したがって、空の文字列を返すための文字列の特別な処理が用意されています。

19
nawfal

良い答えですが、ドットネットコンパクトフレームワークでは使用できません。 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);
    }
}
4
Autodidact