3

リフレクションを使用してジェネリック クラスの非ジェネリック メソッドとジェネリック メソッドを区別するのに問題があります。私が取り組んでいるテストケースは次のとおりです。

public class Foo<T>
{
  public string Bar( T value ) { return "Called Bar(T)"; }

  public string Bar( int value ) { return "Called Bar(int)"; }

  public static void CallBar<TR>(Foo<TR> foo)
  {
      var fooInfo = foo.GetType()
         .GetMethods()
         .Where(x => !x.IsGenericMethod && // doesn't filter out Bar(T)!
                 x.Name == "Bar" &&
                 x.GetParameters().First().ParameterType == typeof(int))
         // !Two identical MethodInfo results, how to choose between them?
         // Is there a gauranteed canonical ordering? Or is it undefined?
         .First();

      Console.WriteLine(fooInfo.Invoke(foo, new object[]{ 0 }));
  }
}

// prints Bar(T)...
Foo<int>.CallBar( new Foo<int>() );
4

5 に答える 5

3

残念ながら、System.Reflectionは、構築された型のメソッドを、それが構築されたジェネリック型定義の対応するメソッドと相関させる良い方法を提供しません。私が知っている解決策は2つありますが、どちらも完璧ではありません。

解決策#1:静的TypeBuilder.GetMethod。TypeBuilderには静的バージョンのGetMethodがあり、ジェネリック構築型とジェネリック型定義のメソッドのMethodInfoを受け入れ、指定されたジェネリック型の対応するメソッドを返します。この例では、を呼び出すTypeBuilder.GetMethod(Foo<int>, Foo<T>.Bar(T))Foo<int>.Bar(T-as-int)、それとの間の曖昧性解消に使用できることがわかりますFoo<int>.Bar(int)

(上記の例は当然コンパイルされません。私はそれぞれのTypeオブジェクトとMethodInfoオブジェクトを使用Foo<int>しましFoo<T>.Bar(T)た。これらは簡単に入手できますが、例が複雑になりすぎます)。

悪いニュースは、これがジェネリック型定義がTypeBuilderである場合、つまりジェネリック型を発行している場合にのみ機能することです。

解決策#2:MetadataToken。ジェネリック型定義からジェネリック構築型への移行時に型メンバーがMetadataTokenを保持することはほとんど知られていない事実です。したがって、あなたの例では、同じMetadataTokenを共有する必要がありますFoo<T>.Bar(T)Foo<int>.Bar(T-as-int)それはあなたがこれをすることを可能にするでしょう:

var barWithGenericParameterInfo = typeof(Foo<>).GetMethods()
   .Where(mi => mi.Name == "Bar" && 
          mi.GetParameters()[0].ParameterType.IsGenericParameter);

var mappedBarInfo = foo.GetType().GetMethods()
    .Where(mi => mi.MetadataToken == genericBarInfo.MetadataToken);

(私が非常に幸運で、最初にそれを正しくすることができたのでない限り、これもコンパイルされません:))

このソリューションの問題は、MetadataTokenがそのためのものではなく(おそらく、ドキュメントが少しざらざらしている)、汚いハックのように感じることです。それにもかかわらず、それは動作します。

于 2009-06-12T13:28:17.513 に答える
1

Foo<int> を使用する場合、Bar(T) メソッドは Bar(int) として型指定され、パラメーターとして定義された int を持つメソッドと区別されません。

Bar(T) の正しいメソッド定義を取得するには、typeof(Foo<int>) の代わりに typeof(Foo<>) を使用できます。

これにより、両者の違いを見分けることができます。次のコードを試してください。


    public static void CallBar<TR>(Foo<TR> foo)
    {
        Func<MethodInfo, bool> match = m => m.Name == "Bar";

        Type fooType = typeof(Foo<>);
        Console.WriteLine("{0}:", fooType);
        MethodInfo[] methods = fooType.GetMethods().Where(match).ToArray();

        foreach (MethodInfo mi in methods)
        {
            Console.WriteLine(mi);
        }

        Console.WriteLine();

        fooType = foo.GetType();
        Console.WriteLine("{0}:", fooType);
        methods = fooType.GetMethods().Where(match).ToArray();

        foreach (MethodInfo mi in methods)
        {
            Console.WriteLine(mi);
        }
    }

これは出力されます:

System.String Bar(T)
System.String Bar(Int32)

System.String Bar(Int32)
System.String Bar(Int32)

于 2009-06-11T23:16:05.847 に答える
0

ドキュメントによると、ContainsGenericParameters が探しているものだと思います。

http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.isgenericmethod.aspx http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.containsgenericparameters.aspx

于 2009-06-11T23:10:13.740 に答える