web-dev-qa-db-ja.com

インスタンス化されたSystem.Typeをジェネリッククラスの型パラメーターとして渡す

タイトルは不明瞭です。私が知りたいのは、これが可能かどうかです:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

明らかに、MyGenericClassは次のように記述されます。

public class MyGenericClass<T>

現在、コンパイラは「型または名前空間「myType」が見つかりませんでした」と文句を言います。これを行う方法がなければなりません。

170
Robert C. Barth

リフレクションなしでこれを行うことはできません。ただし、canはリフレクションで行います。完全な例を次に示します。

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

注:ジェネリッククラスが複数のタイプを受け入れる場合は、タイプ名を省略するときにコンマを含める必要があります。次に例を示します。

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);
207
Jon Skeet

残念ながらありません。ジェネリック引数は、コンパイル時に1)有効な型または2)別のジェネリックパラメーターとして解決可能でなければなりません。リフレクションを使用せずに、ランタイム値に基づいて汎用インスタンスを作成する方法はありません。

14
JaredPar

私の要件はわずかに異なっていましたが、うまくいけば誰かを助けるでしょう。構成からタイプを読み取り、ジェネリックタイプを動的にインスタンス化する必要がありました。

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

最後に、これをどのように呼び出すかを示します。 バックティックでタイプを定義

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);
2
Master P

はさみコードで実行する追加の方法。次のようなクラスがあるとします

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

実行時にFooContentがあるとします

Ifコンパイル時にバインドできた

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

ただし、実行時にこれを行うことはできません。実行時にこれを行うには、次の行に沿って行います。

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

Markdown(IEnumerable<FooContent> contents)を動的に呼び出すには

new Encoder().Markdown( (dynamic) dynamicList)

メソッド呼び出しでのdynamicの使用に注意してください。実行時にdynamicListList<FooContent>になります(さらにIEnumerable<FooContent>でもあります)。これは、ダイナミックの使用も強く型付けされた言語に根ざしているため、ランタイムバインダーは適切なMarkdownを選択します方法。正確に一致するタイプがない場合は、オブジェクトパラメータメソッドを探し、一致するメソッドがない場合はランタイムバインダー例外が発生します。

このアプローチの明らかな欠点は、コンパイル時に型の安全性が大きく失われることです。それでも、これらの行に沿ったコードを使用すると、実行時に期待どおりに完全に入力されるという非常に動的な意味で操作できます。

2
Chris Marisic

どのタイプが渡されるかがわかっている場合は、リフレクションなしでこれを実行できます。 switchステートメントが機能します。明らかに、これは限られた数のケースでのみ機能しますが、リフレクションよりもはるかに高速です。

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}
0
Todd Skelton

このスニペットでは、動的に作成されたリストを作成および使用する方法を示します。たとえば、ここで動的リストに追加しています。

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

同様に、リスト上の他のメソッドを呼び出すことができます。

0
EGN