リフレクションを使用して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

頭を上げてくれてありがとう、しかし私はすでに文字列と基本的なタイプを別々に扱っています。
アイスティーナ

回答:


142

私は最初にこの回答をここに投稿しましたが、これはまったく同じ質問ではないが同じ回答があるため、ここに転載します:

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;
        }
    }
}

すごい、それがまさに私が必要としているもののようです。初期化されていないとは、そのすべてのメモリがゼロに設定されることを意味すると思いますか?(構造体がインスタンス化される方法に似ています)
Aistina '24

各タイプのデフォルト値が何であれ、デフォルトになります。したがって、オブジェクトはnull、ints 0などになります。クラスレベルの初期化は発生すると思いますが、コンストラクターは実行されません。
Jason Jackson、

14
@JSBangs、それはあなたが完全に正当な答えをしているのは嫌です。あなたのコメントと他の回答は、実際に尋ねられた質問に対応していません。あなたがより良い答えを持っているように感じたら、それを提供してください。しかし、私が提供した答えは、他のシリアル化クラスがこのコードを使用するのと同じ方法で、ドキュメント化されたクラスを使用する方法を強調しています。
ジェイソンジャクソン

21
@JSBangs FormatterServices(msdn.microsoft.com/en-us/library/…)は文書化されていません。
Autodidact 2010


72

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

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

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

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


1
このソリューションは問題を単純化しすぎます。タイプがわからず、「このType変数にタイプのオブジェクトを作成するだけ」と言っている場合はどうなりますか?
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;
}

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

あれを呼べ:

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

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


1
誰かのコードの1行を見ると1日を節約できるのは奇妙です。ありがとうございます!パフォーマンス上の理由で私はあなたの投稿に連れて行ってトリックを行いました:) FormatterServicesとActivatorクラスは、コンパイルされた式と比較してパフォーマンスが低いです。
jmodrak 2013年

@nawfal文字列の特別な処理について、この特別な処理なしでは文字列が失敗することはわかっていますが、知りたいのですが、他のすべての型で機能しますか?
Sнаđошƒаӽ

@Sнаđошƒаӽ残念ながらありません。与えられた例はベアボーンであり、.NETには多くの異なるタイプのタイプがあります。たとえば、デリゲート型を渡した場合、どのようにしてインスタンスを与えるのでしょうか。または、コンストラクタがスローした場合、それについて何ができますか?それを処理する多くの異なる方法。それ以来、私のライブラリでより多くのシナリオを処理できるように、これに答えました。現時点ではどこにも公開されていません。
nawfal

4

良い答えですが、ドットネットコンパクトフレームワークでは使用できません。ここに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
これは、デフォルト以外のコンストラクターを呼び出す方法です。コンストラクターをまったく呼び出さずにオブジェクトを作成したいと思ったことはありません。
ロリーマクラウド

2
カスタムシリアライザーを作成している場合は、コンストラクターを呼び出さずにオブジェクトを作成することができます。
Autodidact 2010

1
うん、それはこの質問の正確なユースケースシナリオです:)
Aistina

1
@Aistinaおそらく、この情報を質問に追加できますか?ほとんどの人は、アクターを呼び出さずにオブジェクトを作成することに反対し、それについて時間をかけて議論しますが、実際のユースケースはそれを正当化するので、質問自体に非常に関連があると思います。
julealgon 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.