3

これが私のクラスです:

public class MyClass<T>
{
    public void MyMethod(T a)
    {
    }

    public void MyMethod(int a)
    {
    }
}

のジェネリック型パラメーターがである場合でも、どのようにInvoke MyMethod(T)使用しますか?ReflectionintMyClass

これを行う単一のステートメントがあります(非効率かもしれませんが、簡潔さが好きです):

var mc = new MyClass<int>();

typeof(MyClass<int>).GetMethods().ElementAt(
    typeof(MyClass<int>).
    GetGenericTypeDefinition().
    GetMethods().ToList().FindIndex(m =>
        m.Name.Equals("MyMethod") && 
        m.GetParameters().Count() == 1 &&
        m.GetParameters()[0].ParameterType.IsGenericParameter)
    ).Invoke(mc, new object[] { 1 });
4

2 に答える 2

4

改訂された回答

さて、IsGenericParameterプロパティがfalseと評価される理由は、の明示的な型を使用してMyClass型を作成していたためだと思います<T>

コンパイラーはパラメーターの型を新しくしたのでa(クラスのインスタンス化から推測)、コンパイラーはパラメーターを非ジェネリック型として扱っていたと思います。

また、MSDNで読んでいたことに基づいて、ParameterType.IsGenericParameterプロパティとType.IsGenericTypeプロパティMyMethod<T>()は、 vs 。のようなメソッドがある場合にのみtrueと評価されると思いますMyMethod(T a)。ここで、の型<T>は、クラスでインスタンス化された型から推測されます。 。

これを示す小さなプログラムは次のとおりです。

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

namespace GenericParametersViaReflectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Note: we're using the type without specifying a type for <T>.
            var classType = typeof(MyClass<>);

            foreach (MethodInfo method in classType.GetMembers()
                .Where(method => method.Name == "MyMethod"))
            {
                // Iterate through each parameter of the method
                foreach (var param in method.GetParameters())
                {
                    // For generic methods, the name will be "T" and the FullName
                    // will be null; you can use which ever check you prefer.
                    if (param.ParameterType.Name == "T"
                        || param.ParameterType.FullName == null)
                        Console.WriteLine("We found our generic method!");
                    else
                        Console.WriteLine("We found the non-generic method:");

                    Console.WriteLine("Method Name: {0}", method.Name);
                    Console.WriteLine("Parameter Name: {0}", param.Name);
                    Console.WriteLine("Type: {0}", param.ParameterType.Name);
                    Console.WriteLine("Type Full Name: {0}",
                        param.ParameterType.FullName ?? "null");
                    Console.WriteLine("");
                }   
            }
            Console.Read();
        }
    }
    public class MyClass<T>
    {
        public void MyMethod(T a) { }
        public void MyMethod(int a) { }
    }
}

そして、最終的に得られる結果は次のとおりです。

見つけた、または一般的な方法!
メソッド名:MyMethod
パラメーター名:a
タイプ:T
タイプフルネーム:null

非ジェネリックメソッドが見つかりました:
メソッド名:MyMethod
パラメーター名:a
タイプ:Int32
タイプフルネーム:System.Int32

特定のタイプを使用してクラスのインスタンスを作成する必要がある場合は、Activator.CreateInstanceクラスも役立つ場合があります。


元の回答

明示的に設定されたメソッドの1つ(たとえば、Int32メソッド)に一致する同じデータ型のパラメーターを渡すと、コンパイラーは、汎用パラメーターを受け入れるメソッドよりも自動的にそれを選択すると思います。それ以外の場合、ジェネリックメソッドはコンパイラによって選択されます。

ただし、選択するメソッドを制御できるようにする場合は、次のように、同一のシグニチャを維持しながら、各メソッドを変更して異なるパラメータ名を使用できます。

public class MyClass<T>
{
    public void MyMethod(T genericA) {}
    public void MyMethod(int intA) {}
}

次に、名前付きパラメーターを使用して、次のように目的のメソッドを明示的に呼び出すことができます。

var foo = new MyClass<int>();
foo.MyMethod(genericA: 24); // This will call the "MyMethod" that only accepts <T>.
foo.MyMethod(intA: 19); // This will call the "MyMethod" that only accepts integers.

編集

何らかの理由で、私の元の回答では、リフレクションを使用して言及した部分を見逃しましたが、私の元の回答をこれらの他の回答と組み合わせて、実行可能な解決策を提供できるようです。

于 2012-11-04T03:11:18.220 に答える
3

私がこれを行うことができた唯一の方法は、で使用することでしGetGenericTypeDefinitionIsGenericParameter。ジェネリック型の定義では、1つのメソッドがIsGenericParameterパラメーターでtrueに設定されています。ただし、閉じた型の場合、どのパラメーターもこれをtrueとして持つことはありません。MethodInfo次に、ジェネリック型の定義からメソッドを呼び出すことはできないので、インデックスを保存し、それを使用しMethodInfoて、閉じた型で対応するものを検索しました。

public class Program
{
    public static void Main(string[] args)
    {
        bool invokeGeneric = true;
        MyClass<int> b = new MyClass<int>();
        var type = b.GetType().GetGenericTypeDefinition();
        int index = 0;
        foreach(var mi in type.GetMethods().Where(mi => mi.Name == "MyMethod"))
        {
            if (mi.GetParameters()[0].ParameterType.IsGenericParameter == invokeGeneric)
            {
                break;
            }
            index++;
        }

        var method = b.GetType().GetMethods().Where(mi => mi.Name == "MyMethod").ElementAt(index);
        method.Invoke(b, new object[] { 1 });
    }
}

public class MyClass<T>
{
    public void MyMethod(T a)
    {
        Console.WriteLine("In generic method");
    }

    public void MyMethod(int a)
    {
        Console.WriteLine("In non-generic method");
    }
}
于 2012-11-04T05:31:05.080 に答える