web-dev-qa-db-ja.com

実行時にのみ既知の型引数を使用してジェネリックメソッドを呼び出す

編集:

もちろん、私の実際のコードはこのようには見えません。私がやりたいことをより明確にするために、準疑似コードを書きました。

代わりに物事を台無しにしたように見えます。

だから、私が実際にやりたいのはこれです:

Method<Interface1>();
Method<Interface2>();
Method<Interface3>();
...

まあ...私は反射を使用してループに変えることができると思いました。質問は次のとおりです。 very反射に関する浅い知識を持っています。したがって、コード例は素晴らしいでしょう。

シナリオは次のようになります。

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in Assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var i in interfaces)
    {
        Method<i>(); // Get compile error here!
    }




元の投稿:

こんにちは!

私は名前空間内のすべてのインターフェイスをループし、次のような一般的なメソッドへの引数として送信しようとしています:

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in Assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var interface in interfaces)
    {
        Method<interface>(); // Get compile error here!
    }
}

私が受け取るエラーは、「タイプ名が必要ですが、ローカル変数名が見つかりました」です。私が試してみると

...
    foreach(var interface in interfaces)
    {
        Method<interface.MakeGenericType()>(); // Still get compile error here!
    }
}

「演算子 '<'をタイプ 'method group'および 'System.Type'のオペランドに適用できません」というメッセージが表示されるこの問題を回避する方法についてのアイデアはありますか?

121
Presidenten

編集:さて、短いが完全なプログラムの時間です。基本的な答えは以前と同じです。

  • Type.GetMethodを使用して「オープン」汎用メソッドを検索します
  • MakeGenericMethodを使用してジェネリックにする
  • Invokeで呼び出す

サンプルコードを次に示します。クエリ式をドット表記に変更したことに注意してください。基本的にwhere句を取得しただけでクエリ式を使用しても意味がありません。

using System;
using System.Linq;
using System.Reflection;

namespace Interfaces
{
    interface IFoo {}
    interface IBar {}
    interface IBaz {}
}

public class Test
{
    public static void CallMe<T>()
    {
        Console.WriteLine("typeof(T): {0}", typeof(T));
    }

    static void Main()
    {
        MethodInfo method = typeof(Test).GetMethod("CallMe");

        var types = typeof(Test).Assembly.GetTypes()
                                .Where(t => t.Namespace == "Interfaces");

        foreach (Type type in types)
        {
            MethodInfo genericMethod = method.MakeGenericMethod(type);
            genericMethod.Invoke(null, null); // No target, no arguments
        }
    }
}

元の回答

最初に変数「インターフェース」を呼び出すことの明らかな問題はさておきましょう。

リフレクションで呼び出す必要があります。ジェネリックのポイントは、compile時に型チェックを追加することです。コンパイル時にその型がわからないため、ジェネリックを使用する必要があります。

ジェネリックメソッドを取得し、そのメソッドでMakeGenericMethodを呼び出してから呼び出します。

あなたのインターフェース型自体は実際にジェネリックですか? MakeGenericTypeを呼び出していますが、型引数を渡していないので、私は尋ねます...

Method<MyNamespace.Interface<string>>(); // (Or whatever instead of string)

または

Method<MyNamespace.Interface>();

後者の場合、MakeGenericTypeではなく、MakeGenericMethodの呼び出しのみが必要です。

143
Jon Skeet