2015-10-19 64 views
2

我已经为一个自定义泛型类(TabularList<>)创建了一个通用JavaScriptConverter派生,我已将其命名为ITabularConverter<>ITabularConverter使用反射来检索从TabularList<>泛型类型定义派生的所有封闭类型,以通知JavaScriptSerializer它能够转换所有闭合类型ITabularConverter<>。该代码看起来是这样的:为什么封闭泛型不能由Assembly.GetTypes()返回?

public override IEnumerable<Type> SupportedTypes 
{ 
    get 
    { 
     var type = typeof (TabularList<>); 
     var itabulars = Assembly.GetAssembly(type).GetTypes() 
      .Where(t => t.IsGenericType 
       && t.GetGenericTypeDefinition() == type); 
     return itabulars; 
    } 
} 

的问题是,即使有这个代码执行的时间存在至少一个封闭式TabularList <>的,只有开放式泛型类型定义是由上述返回码。当我将搜索扩展到所有当前加载的程序集时也是如此。

更奇怪的是,如果我检查调用堆栈,我可以看到JavaScriptSerializer.Serialize方法被调用的位置,并使用立即窗口检查被序列化的对象并证明存在通用定义的封闭版本。然而,当我在立即窗口执行下面的代码,结果是false

Assembly.GetAssembly(obj.TabularListProp.GetType()) 
    .GetTypes() 
    .Contains(obj.TabularListProp.GetType()); 

所以我找回其中封闭的通用定义,然后寻找通过定义的类型中,封闭式泛型类型的程序集该组件,并找不到封闭类型。这有什么意义呢?

这里是TabularList<>声明:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Xml.Serialization; 

namespace Central.Claims.UX.ClaimWFMgrViewModel 
{ 
    [Serializable] 
    public class TabularList<T> : List<T>, ITabular 
    { 

     private List<List<object>> _tableView; 

     [XmlIgnore] 
     public List<List<object>> TableView 
     { 
      get { return GetTableView(); } 
     } 

     private List<KeyValuePair<string, Func<object, object>>> Schema { get; set; } 
     public TabularList() 
     { 
      Initialize(); 
     } 

     public TabularList(IEnumerable<T> source) : base(source) 
     { 
      Initialize(); 
     } 

     private void Initialize() 
     { 
      RefreshTableView = true; 
      var type = typeof(T); 

      if (Schemas.ContainsKey(type.Name)) 
      { 
       Schema = Schemas[type.Name]; 
      } 
      else 
      { 
       Schema = new List<KeyValuePair<string, Func<object, object>>>(); 
      } 
     } 

     protected List<List<object>> GetTableView() 
     {  
      GetSchema(); 
      BuildTable(); 

      return _tableView; 
     } 

     private void GetSchema() 
     { 
      if (this.Any()) 
      { 
       var properties = this.First().GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 

       foreach (var property in properties) 
       { 
        var getter = property.GetGetMethod(); 

        Schema.Add(new KeyValuePair<string, Func<object, object>>(
         property.Name, 
         (Func<object, object>) Delegate.CreateDelegate(typeof (Func<object, object>), getter) 
         )); 
       } 
      } 
     } 

     private void BuildTable() 
     { 
      _tableView = new List<List<object>>(); 

      foreach (var item in this) 
      { 
       TableView.Add(ToTableRow(item)); 
      } 
     } 

     private List<object> ToTableRow(T item) 
     { 
      var row = new List<object>(); 

      foreach (var column in Schema) 
      { 
       row.Add(column.Value(item)); 
      } 

      return row; 
     } 
    }  
} 

基于这里提供的答案,我已经改写在这个问题上的SO质疑How to retrieve a list of all closed generic types generated by the .NET runtime?

+0

是否型,其中'TabularList <>'类型是相同的组件存在:您可以通过在类型的主叫GetGenericTypeDefinition()得到泛型类型定义? – DavidG

+0

发布您的类声明,并确定'GetTypes'不会返回它吗?或者你的where条件过滤它?对于不带任何类型参数的类型,IsGenericType将为false。 –

+0

@DavidG - 不,类型是在同一解决方案的另一个项目中定义的,而这些代码片段位于Web应用程序中。 –

回答

1

记住反射只是查询,所以其中包含的任何信息纯粹是编译型信息。事实上,你有一个TabularList<SomeType>)实例确实而不是更改定义它的程序集中包含的元数据。

封闭泛型类型是而不是定义在定义开放泛型类型的程序集或创建该特定闭合类型的程序集中。

您期待为找到所有可能的元数据关闭定义List<T>mscorlib?你会期望在创建List<int>变量的程序集中找到它吗?

注意,它确实工作的另一种方式 - 如果你打电话

Assembly a = Assembly.GetAssembly(typeof(List<int>)); 

你得到大会

mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 

所以,你可能能够扭转你的逻辑 - 而不是搜索程序集中的所有关闭类型,查找关闭类型的程序集以查看它是否“受支持”。

+0

*打了额头*它只是想到,我在其他类似的StackOverflow问题中看到的封闭类型的反射示例涉及在编译时关闭的泛型类型。这一切都突然有道理。那么有没有什么方法可以查询运行时的泛型类型的所有封闭定义? –

+0

@DanielArant我不确定你的意思 - 你能发表一个你的类型的例子,“在编译时关闭”吗?你也可以在你的文章中修剪很多不相关的方法。 –

+0

不幸的是,JavaScriptConverter的本质是,只有在被转换的类型和SupportedTypes属性返回的类型之一相等时,它才会使用转换器。 –

1

通用类型通常不存在于程序集中。如果是这样的话,那么类型参数的可能组合将需要存在,这很快就会给你无限量的不同类型。

因此,通用类型定义是组件中存在的具体类型。

Type t = typeof(List<int>); 
t.Assembly.GetTypes().Contains(t); // false 
t.Assembly.GetTypes().Contains(t.GetGenericTypeDefinition()); // true 
+0

这与@ DStanley的答案是一致的。有没有办法动态检索由运行时生成的关闭类型列表?如果SupportedTypes属性返回的类型之一等于当前正在转换的类型,那么JavaScriptSerializer将仅使用转换器,因此不足以返回泛型类型定义。 –

+0

“运行时生成的关闭类型”是什么意思?泛型类型参数在编译时插入(除非使用反射)。如果你想从'X <>'生成所有可能的'X ',那么你应该以某种方式注释'T',这样你就可以识别它们,然后从这个列表中建立'X '。 – poke

+1

根据MSDN文档,运行时创建封闭泛型类型:https://msdn.microsoft.com/en-us/library/f4a6ta2h.aspx –