2013-03-26 9 views
6

私はジェネリックの奇妙な振る舞いを経験しました。以下は私がテストに使用するコードです。一般的な奇妙な振る舞い

public static class Program 
{ 
    public static void Main() 
    { 
     Type listClassType = typeof(List<int>).GetGenericTypeDefinition(); 
     Type listInterfaceType = listClassType.GetInterfaces()[0]; 

     Console.WriteLine(listClassType.GetGenericArguments()[0].DeclaringType); 
     Console.WriteLine(listInterfaceType.GetGenericArguments()[0].DeclaringType); 
    } 
} 

出力:

System.Collections.Generic.List`1[T] 
System.Collections.Generic.List`1[T] 

私は、ジェネリック型定義を使用するため、2番目のConsole.WriteLineを呼び出し、クラスではなくインタフェースを表示すること、それは非常に奇妙ました。これは正しい動作ですか?

私はコンパイラで汎用型の推論を実装しようとしています。次のコードがあるとします。

public static class GenericClass 
{ 
    public static void GenericMethod<TMethodParam>(IList<TMethodParam> list) { } 
} 

そして、私は次のようにこのメソッドを呼び出したい:

GenericClass.GenericMethod(new List<int>()); 

推論の可能性を確認するために、私は、メソッドのシグネチャタイプを比較する必要があり、引数の型が渡されました。しかし、以下のコードはfalseを返します。

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType; 

このような比較には常にType.GetGenericTypeDefinitionを使用する必要がありますか?

+0

まあ、いずれの場合でも、バインドされていないジェネリックリストからコールチェーンを調達しているため、宣言型は常にそのバインドされていないジェネリック型になります。あなたは何をしようとしているのですか? – JerKimball

答えて

15

あなたは両方のT.命名されている2つの異なる種類が混乱しているが、このようにそれについて考える:

interface IFoo<TIFOO> { } 
class Foo<TFOO> : IFoo<TFOO> {} 

OK、Foo<int>ジェネリック型定義は何ですか?それはFoo<TFOO>です。

Foo<TFOO>によって実装されたインターフェイスは何ですか?それはIFoo<TFOO>です。

タイプの引数Foo<TFOO>とは何ですか?明らかにTFOO

どのタイプがと宣言されましたかTFOOFoo<TFOO>がそれを宣言しました。

タイプの引数IFoo<TFOO>とは何ですか?明らかにTFOO,ではないTIFOO

どのタイプがと宣言されましたかTFOOFoo<TFOO>がそれを宣言しました。 は、IFoo<TFOO>ではありません。 TFOOFooからです。

意味がありますか?

私は私のコンパイラにジェネリック型推論を実装しようとしている...

をこうして私はあなたが使用していると仮定します。

+0

私は "System.Collections.Generic.List'1 [T]'としてインタフェースタイプが印刷される理由について、 "どのタイプ* TFOO"を宣言したかの手順に従いません。 MSDNの例では、* interfaces *の名前は* concrete型*で表示されます。 'TFOO'を宣言した人がなぜインタフェースの名前ではなくクラスの名前を出力するのかという理由はわかりません。 http://msdn.microsoft.com/en-us/library/system.type.getinterfaces.aspx –

+0

@EricJ .:あなたのリンクの例は '.DeclaringType'を使用しません。 – recursive

+0

@再帰的:私は知っていますが、私はまだそのステップを理解していません。エリック・リッペルトは彼の言うことが正しいと私は確信しています。 –

2

あなたは2番目の質問を追加したため、2番目の回答を追加しますリフレクションを使用してコンパイラを構築します。これは良い考えではないかもしれません。初期の段階に戻っていたよりもはるかにパフォーマンスが向上していますが、トークンを使用して直接作業するのに比べてまだ重いです。また反射エミットは、可能なあらゆるタイプのトポロジーを放出することができません。ネストされた構造体の型を含むいくつかのシナリオではうんざりしてしまいます。

代わりにCCIの使用を検討します。 RoslynではCCIの修正版を使用しました。

以下のコードはfalseを返します。正しいです

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType 

。パラメータタイプはIList<TMethodParam>であり、listInterfaceTypeIList<T>です。Tは、List<T>で宣言された汎用パラメータタイプであり、IList<T>で宣言された汎用パラメータタイプではありません。それらはすべて異なるタイプです。

このような比較には常にType.GetGenericTypeDefinitionを使用する必要がありますか?

2つのジェネリック型が両方とも同じジェネリック型の構成であるかどうかを確認する場合は、「はい」を選択します。それがあなたがチェックしたいものでないならば、いいえ。

このタイプのシステムは複雑なので、非常に注意してください。

これは、リフレクションタイプのオブジェクトベースのアプローチではなく、トークンベースのアプローチを行うもう1つの理由です。手元にトークンがある場合は、TypeDefTypeRefを区別する方がずっと簡単です。