【问题标题】:Why would a closed generic type not be returned by Assembly.GetTypes()?为什么 Assembly.GetTypes() 不会返回封闭的泛型类型?
【发布时间】:2015-10-19 23:56:48
【问题描述】:

我为自定义泛型类 (TabularList<>) 创建了泛型 JavaScriptConverter 派生,我将其命名为 ITabularConverter<>ITabularConverter 使用反射来检索从 TabularList<> 泛型类型定义派生的所有封闭类型,以通知 JavaScriptSerializer 它能够转换 ITabularConverter<> 的所有封闭类型。该代码如下所示:

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&lt;&gt;的声明:

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? 中重新表述了这个问题

【问题讨论】:

  • 该类型是否存在于TabularList&lt;&gt;类型所在的同一程序集中?
  • 发布您的班级声明,您确定GetTypes 不会返回吗?或者你的 where 条件过滤它?对于不带任何类型参数的类型,IsGenericType 将为 false。
  • @DavidG - 不,类型是在同一解决方案的另一个项目中定义的,而这些代码 sn-ps 是在 Web 应用程序中。
  • @SriramSakthivel - 发布班级声明。在这种情况下,我只是使用GetTypes().Contains(type)。当我最初发布问题时,我使用的是 Linq,但我切换到 .Contains(),因为即时窗口无法处理 lambda 表达式。
  • Type.Assembly 属性的文档说:“如果当前 Type 对象表示构造的泛型类型,则此属性返回包含泛型类型定义的程序集。” Assembly.GetAssembly(Type) 方法的文档没有这样说,但它应该这样做,因为它做同样的事情。 (这只是糟糕的文档)

标签: c# .net generics reflection


【解决方案1】:

请记住,反射只是查询元数据,因此其中包含的任何信息都是纯粹的编译类型信息。您拥有TabularList&lt;SomeType&gt;) 的实例这一事实不会更改定义它的程序集中包含的元数据。

封闭的泛型类型没有定义在定义开放泛型类型的程序集中,也不是在创建该特定封闭类型的程序集中。

您是否希望在mscorlib 内找到List&lt;T&gt;所有可能封闭定义的元数据?您是否希望在创建 List&lt;int&gt; 变量的程序集中找到它?

请注意,它确实以另一种方式工作 - 如果您调用

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

你得到了大会

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

因此,您也许可以颠倒您的逻辑 - 而不是在程序集中搜索所有封闭类型,而是查找封闭类型的程序集以查看它是否“受支持”。

【讨论】:

  • smacks forehead 我突然意识到,我在其他类似的 StackOverflow 问题中看到的反思封闭类型的示例涉及在编译时关闭的泛型类型。这一切突然变得有意义。那么有没有办法在运行时查询泛型类型的所有封闭定义?
  • @DanielArant 我不确定您的意思-您能否发布一个“在编译时关闭”的类型示例?您可能也可以删除帖子中的许多不相关的方法。
  • 不幸的是,JavaScriptConverter 的本质是只有在被转换的类型与 SupportedTypes 属性返回的类型之一相等时才会使用转换器。
  • “在编译时关闭”我只是指在派生类型定义中提供类型参数,例如CloseGeneric : List&lt;int&gt;
  • @DanielArant 如果您有一个派生自泛型类型的非泛型类,那么您可以在程序集的类型列表中找到该类型。
【解决方案2】:

程序集中通常不存在泛型类型。如果是这种情况,那么类型参数的每个可能的组合都需要存在,这很快就会为您提供无限数量的不同类型。

因此,泛型类型定义 是程序集中存在的具体类型。您可以通过在类型上调用GetGenericTypeDefinition() 来获取泛型类型定义:

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

【讨论】:

  • 这与@DStanley 的回答相同。有没有办法动态检索运行时生成的封闭类型列表? JavaScriptSerializer 将仅在 SupportedTypes 属性返回的类型之一等于当前正在转换的类型时才使用转换器,因此返回泛型类型定义还不够好。
  • “运行时生成的封闭类型”是什么意思?泛型类型参数在编译时插入(除非您使用反射)。如果您想从X&lt;&gt; 生成所有可能的X&lt;T&gt;,那么您应该以某种方式注释Ts,以便您可以识别它们,然后从该列表中构建X&lt;T&gt;s。
  • 根据 MSDN 文档,封闭的泛型类型是由运行时创建的:msdn.microsoft.com/en-us/library/f4a6ta2h.aspx
猜你喜欢
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
相关资源
最近更新 更多