C#では、メソッド内で渡されたジェネリック型をインスタンス化する方法を教えてください。


98

InstantiateType<T>以下のメソッド内でタイプTをインスタンス化するにはどうすればよいですか?

私はエラーを取得しています:「T」「は型パラメータ」ですが、「変数」のように使用されます。

(修正された回答のスクロールダウン)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Console.WriteLine(container.InstantiateType<Customer>("Jim", "Smith"));
            Console.WriteLine(container.InstantiateType<Employee>("Joe", "Thompson"));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson
        {
            T obj = T();
            obj.FirstName(firstName);
            obj.LastName(lastName);
            return obj;
        }

    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

修正された回答:

すべてのコメントをありがとう、彼らは私を正しい軌道に乗せました、これは私がやりたかったことです:

using System;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Customer customer1 = container.InstantiateType<Customer>("Jim", "Smith");
            Employee employee1 = container.InstantiateType<Employee>("Joe", "Thompson");
            Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1));
            Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
        {
            T obj = new T();
            obj.FirstName = firstName;
            obj.LastName = lastName;
            return obj;
        }
    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class PersonDisplayer
    {
        private IPerson _person;

        public PersonDisplayer(IPerson person)
        {
            _person = person;
        }

        public string SimpleDisplay()
        {
            return String.Format("{1}, {0}", _person.FirstName, _person.LastName);
        }

        public static string SimpleDisplay(IPerson person)
        {
            PersonDisplayer personDisplayer = new PersonDisplayer(person);
            return personDisplayer.SimpleDisplay();
        }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

+1は、より良いデザインパターンにシフトします。
Joel Coehoorn、2009年

非常にきちんと型付けされたコードの+1はまれです。
nawfal 2013

回答:


131

次のようにメソッドを宣言します。

public string InstantiateType<T>(string firstName, string lastName) 
              where T : IPerson, new()

最後に追加の制約があることに注意してください。次にnew、メソッド本体にインスタンスを作成します。

T obj = new T();    

4
私は長年C#を書いていて、ジェネリック型の乱用がひどいので、ジェネリック型をインスタンス化するためにこのような制約を定義できるとは知りませんでした。どうもありがとう!
Nicolas Martel

とってもとっても素敵!!
Sotiris Zegiannis

タイプが指定されていない場合はどうなりますか?
jj

31

いくつかの方法。

タイプを指定しない場合は、コンストラクターが必要です。

T obj = default(T); //which will produce null for reference types

コンストラクターで:

T obj = new T();

しかし、これには次の条項が必要です。

where T : new()

1
最初のものは、参照型のインスタンスを作成するのではなく、nullを割り当てます。
Joel Coehoorn、2009年

1
うん。デフォルトのコンストラクタなしでタイプを作成するには、リフレクションを使用する必要があります。default(T)はすべての参照タイプでnullです。
ダンC.

1
はい、もちろん、完全性のために含まれています。
アナカタ2009年

13

上記の答えをさらに拡張するには、 where T:new()、ジェネリックメソッドに制約をするには、Tにパラメーターのないパブリックコンストラクターが必要です。

それを避けたい場合、そしてファクトリーパターンでは、コンストラクターを直接ではなく、ファクトリーメソッドを他のユーザーに強制する場合があります-代わりに、リフレクション(Activator.CreateInstance...)を使用してデフォルトのコンストラクターをプライベートに保つことです。ただし、これには当然パフォーマンスの低下が伴います。


人々が「その他すべての回答」に反対票を投じるのはこれが初めてではありません:)
Dan C.

dusgtが質問に落ち着くまで、「競合する」回答を不規則に投票しないことを認めます。DI推測(非ポイント)カルマがそれらを整理します!
Ruben Bartelink 2009年

8

あなたが望む新しい Tを()、しかし、あなたはまた、追加する必要があります, new()whereファクトリメソッドのための仕様


私はそれを元に戻しました、私はそれを理解し、助けました、一般的に人々はここの説明よりもコードを投稿したいようです
Edward Tanguay

ありがとう、世界は再び理にかなっています!
Ruben Bartelink 2009年

正解ですが、あなたの答えは確かに少し短めです;)
Lorenz Lo Sauer

4

少し古いですが、解決策を探している他の人にとって、これはおそらく興味深いでしょう:http : //daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/

2つのソリューション。1つはアクティベーターを使用し、もう1つはコンパイル済みラムダを使用します。

//Person has private ctor
var person = Factory<Person>.Create(p => p.Name = "Daniel");

public static class Factory<T> where T : class 
{
    private static readonly Func<T> FactoryFn;

    static Factory()
    {
        //FactoryFn = CreateUsingActivator();

        FactoryFn = CreateUsingLambdas();
    }

    private static Func<T> CreateUsingActivator()
    {
        var type = typeof(T);

        Func<T> f = () => Activator.CreateInstance(type, true) as T;

        return f;
    }

    private static Func<T> CreateUsingLambdas()
    {
        var type = typeof(T);

        var ctor = type.GetConstructor(
            BindingFlags.Instance | BindingFlags.CreateInstance |
            BindingFlags.NonPublic,
            null, new Type[] { }, null);

        var ctorExpression = Expression.New(ctor);
        return Expression.Lambda<Func<T>>(ctorExpression).Compile();
    }

    public static T Create(Action<T> init)
    {
        var instance = FactoryFn();

        init(instance);

        return instance;
    }
}

2

また、リフレクションを使用してオブジェクトのコンストラクターをフェッチし、その方法でインスタンス化することもできます。

var c = typeof(T).GetConstructor();
T t = (T)c.Invoke();

1

ファクトリークラスを使用して、コンパイルされたlamba式でオブジェクトをビルドする:ジェネリック型をインスタンス化するために私が見つけた最速の方法。

public static class FactoryContructor<T>
{
    private static readonly Func<T> New =
        Expression.Lambda<Func<T>>(Expression.New(typeof (T))).Compile();

    public static T Create()
    {
        return New();
    }
}

ベンチマークを設定するために実行した手順は次のとおりです。

ベンチマークテストメソッドを作成します。

static void Benchmark(Action action, int iterationCount, string text)
{
    GC.Collect();
    var sw = new Stopwatch();
    action(); // Execute once before

    sw.Start();
    for (var i = 0; i <= iterationCount; i++)
    {
        action();
    }

    sw.Stop();
    System.Console.WriteLine(text + ", Elapsed: {0}ms", sw.ElapsedMilliseconds);
}

私もファクトリーメソッドを使ってみました:

public static T FactoryMethod<T>() where T : new()
{
    return new T();
}

テストのために私は最も単純なクラスを作成しました:

public class A { }

テストするスクリプト:

const int iterations = 1000000;
Benchmark(() => new A(), iterations, "new A()");
Benchmark(() => FactoryMethod<A>(), iterations, "FactoryMethod<A>()");
Benchmark(() => FactoryClass<A>.Create(), iterations, "FactoryClass<A>.Create()");
Benchmark(() => Activator.CreateInstance<A>(), iterations, "Activator.CreateInstance<A>()");
Benchmark(() => Activator.CreateInstance(typeof (A)), iterations, "Activator.CreateInstance(typeof (A))");

1 000 000反復を超える結果:

新しいA():11ms

FactoryMethod A():275ms

FactoryClass A .Create():56ms

Activator.CreateInstance A():235ミリ秒

Activator.CreateInstance(typeof(A)):157ms

備考.NET Framework 4.5と4.6の両方を使用してテストしました(同等の結果)。


0

タイプをインスタンス化する関数を作成する代わりに

public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
    {
        T obj = new T();
        obj.FirstName = firstName;
        obj.LastName = lastName;
        return obj;
    }

あなたはこのようにそれをすることができたでしょう

T obj = new T { FirstName = firstName, LastName = lastname };

1
これは、尋ねられている質問には答えません。ここでの実際の問題は、ジェネリッククラスの新しいインスタンスを作成する必要があることでした。おそらくそれは意図されていなかったのかもしれませんが、初期化子を使用すると元の問題が解決されると言っているようですが、実際には解決されません。new()制約はまだ仕事にあなたの答えのための一般的なタイプに必要とされています。
ユーザー

ここでイニシャライザが役立つツールであると示唆している場合は、別の回答ではなく、コメントとして投稿してください。
ユーザー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.