2012-10-10 91 views
10

當我列出當前AppDomain中的所有類型時,我會看到泛型類型與泛型佔位符。但是,如果我用一個類型實例化我的泛型類型,然後列出appDomain中的所有類型,我沒有看到新創建的閉合類型。列出運行時從開放泛型類型創建的已關閉類型

在下面的例子中,產量只有:

Foo`1[T] 

我在尋找使密閉型:

Foo`1[System.Int32] 

有沒有辦法看到封閉類型的運行時有基於我的開放泛型類型爲我創建?

class Foo<T> 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tmp = new Foo<int>(); 
     ListTypes(); 
    } 

    private static void ListTypes() 
    { 
     var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from type in assembly.GetTypes() 
         where type.Name.Contains("Foo") 
         select type; 

     foreach (var type in types) 
      Console.WriteLine(type.ToString()); 
    } 
} 

我也試圖找到所有類型的泛型參數,希望發現封閉類型。

class Foo<T> 
{ 
} 

class Bar 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tmp = new Foo<Bar>(); 
     ListTypes(); 
    } 

    private static void ListTypes() 
    { 
     var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from type in assembly.GetTypes() 
         where type.IsGenericType 
         && type.GetGenericArguments().Contains(typeof(Bar)) 
         select type; 

     foreach (var type in types) 
      Console.WriteLine(type.ToString()); 
    } 
} 

這只是爲了滿足我的好奇心。

+0

如果我理解正確,這個反射將簡單地獲取在元數據中定義的類型,在您的情況下只包含泛型類型定義。由於特定類型可以在運行時從泛型類型動態構建(再次使用反射,傳遞泛型參數) - 您可以看到無法將它們放入元數據中......所以,其他一些機制(不是元數據探索)必須用於查找創建的特定類型。 –

+0

我明白了。這就解釋了爲什麼我看不到在運行時創建的類型,它們不在反射查詢的元數據中。我想知道其他機制會是什麼? –

+1

在mscorlib中有一個名爲TypeNameParser的私有類型,它有一個返回字符串數組的GetNames方法,但是當我嘗試在反射下使用它時,我收到致命錯誤,提醒我對COM對象和互操作知之甚少,並且通常我不應該在mscorlib中使用私有類型:-P儘管如此,仍然在尋找一個優雅的解決方案。 –

回答

5

至於我能在這種情況下Foo<T>明白的是一個開放的未綁定泛型類型,所以在運行時,CLR將使用它作爲藍本/骨架構造和關閉泛型類型指定類型參數類型(Foo<int>Foo<object>等)。所以基本上Foo<int>是一個運行時構建的Foo<T>骨架的實現。

現在,在運行時,你可以通過使用typeof(Foo<int>)typeof(Foo<>).MakeGenericType(new[] { typeof(int) })得到Foo<int>類型,它是不一樣的Type,它是沒有意義的它是。但仔細觀察,您會看到typeof(Foo<T>)typeof(Foo<int>)共享相同的元數據標記和GUID。

另一個有趣的事情是,typeof(Foo<int>).Assembly將是你所期望的,但正如你已經注意到,你不能從大會得到這種類型。

這是因爲Foo<int>未在程序集中定義(您可以使用Reflector/ILSpy檢查程序集元數據)。在運行時,CLR將爲Foo<int>(如此構建的無限開放泛型類型定義的封閉類型)創建(「構建」)Foo<T>的專用(「封閉」)版本,並「給它」一個Type。因此,除非CLR直接以某種方式公開它在運行時生成的封閉泛型類型列表,否則您運氣不佳。

而且,這裏是一個可能證實我所說的一個片段:

即使泛型類型,如節點<表> 和節點<字符串>的每個建築,都有其獨特的類型CLR可以在 實例之間重用大量實際的JIT編譯代碼。這極大地減少了代碼膨脹並且可能是 ,因爲泛型的各種實例在 運行時被擴展。所有在編譯時存在的構造類型都是 類型的引用。當組件A和B都引用在第三個程序集中定義的通用類型 時,它們的構造類型在運行時擴展爲 。這意味着除了共享CLR類型標識 (適當時)之外,還可以從程序集A和B中輸入實例,並共享 共享運行時資源,例如本地代碼和擴展元數據。

http://msdn.microsoft.com/en-us/magazine/cc163683.aspx

+2

這是一些沉重的「運行時CLR魔術」:爲每個封閉類型「加載」調用Foo 的靜態構造函數:-) P.S.當你需要他時,Skeet在哪裏? –

1

伊萬的回答大多是對的,但聲稱集元數據不包含構造類型的任何信息是不太正確的。所有構造的類型在使用它們的程序集中定義,並使用像Mono.Cecil這樣的工具讓您看到它。構造類型不會通過反射暴露,甚至Mono.Cecil也很難找到它們。

基本上你必須通過所有類型使用在裝配,例如,屬性類型,返回類型,局部變量類型等。這些信息包含在程序集元數據中,並且可以用Mono.Cecil進行合理的枚舉。然後應用簡單的過濾器來檢測類型是否構造。請注意,您可能必須遍歷幾個引用泛型類型定義的程序集,以便查找從中構建的所有類型。

該解決方案有兩個限制。首先,通過反射構建的類型自然不會出現在任何程序集中。其次,一些構造類型嵌入到泛型類型/方法中,並且它們的泛型類型參數只有在它們的父類型/方法使用特定泛型類型參數實例化後才知道。

相關問題