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


182

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

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

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

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

public class MyGenericClass<T>

現在、コンパイラーは「タイプまたはネームスペース「myType」が見つからなかった」と不平を言っています。」これを行う方法がなければなりません。


ジェネリック!=テンプレート。すべてのジェネリック型変数は、実行時ではなくコンパイル時に解決されます。これは、4.0の「動的」タイプが役立つ可能性がある状況の1つです。

1
@意志-どのように?ジェネリックと一緒に使用すると、現在のCTPでは、基本的に<object>バージョンを呼び出すことになります(私がトリックを逃していない限り...)
Marc Gravell

foo.Method((dynamic)myGenericClass)実行時のメソッドバインディングに使用できる@MarcGravell 。タイプのメソッドオーバーロードのサービスロケータパターンを効果的に使用できます。
Chris Marisic

@ChrisMarisicはい、一部のジェネリックについてpublic void Method<T>(T obj)-そのコメント以降、過去6年間に私が数回以上使用したトリック; p
マークグラベル

@MarcGravellは、メソッドがインスタンス化するようにそれを修正する方法はありますか?
barlop

回答:


220

反射なしではこれを行うことはできません。ただし、反射でそれを行うことができます。ここに完全な例があります:

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

1
これは良いことですが、createdでメソッドを呼び出すにはどうすればよいですか?もっと反射?
ロバートC.バース

7
まあ、ジェネリック型に非ジェネリックインターフェイスを実装させることができれば、そのインターフェイスにキャストできます。または、ジェネリックで実行するすべての作業を行う独自のジェネリックメソッドを記述、リフレクションで呼び出すこともできます。
Jon Skeet、

1
ええ、typeArgument型変数に返された型に関する唯一の情報がある場合、createdの使い方を理解していませんか?あなたはその変数にキャストしなければならないようですが、それが何であるかを知らないので、リフレクションでそれを行うことができるかどうかわかりません。たとえば、オブジェクトがオブジェクト変数として渡された場合、オブジェクトがint型であるかどうか、たとえばこの作業でList <int> 作成された変数はintとして扱われますか?
theringostarrs 2009

6
@ RobertC.Barth「オブジェクト」の代わりに、サンプルタイプ「ダイナミック」で「作成」オブジェクトを作成することもできます。これにより、そのメソッドを呼び出すことができ、評価は実行時まで延期されます。
McGarnagle 2012

4
@balanza:MakeGenericMethodを使用します。
Jon Skeet 2013年

14

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


2

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

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

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

コンパイル時にバインドできた場合、

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

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

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

0

渡されるタイプがわかっている場合は、リフレクションなしでこれを行うことができます。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;
        }
    }
}

何百ものクラスを扱い始めると、これは醜く速くなります。
マイケルg

0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.