为什么通用类型定义实现的接口会丢失类型信息?

Why a generic type definition implemented interfaces lose type information?

例如,如果您 运行 以下代码...

Type IListType = new List<string>().GetType()
                                   .GetInterface("IList`1")
                                   .GetGenericTypeDefinition();

...观察 IListType 变量,您会发现整个 Type 实例具有所有可用的属性,如 FullName 和其他。

但是当你运行下面的代码时会发生什么?

Type IListType2 = typeof(List<>).GetInterface("IList`1")

现在 IListType 从通用类型定义中得到的与第一个代码示例不同:大多数 Type 属性将 return 为 null。

主要问题是 IListType == IListType2 不相等,但它们是同一类型。

怎么回事?

这太丑了...

现在看看如果你调用 IListType2.GetGenericTypeDefinition() 会发生什么......它恢复了类型信息!

如果 .NET Framework 开发团队成员可以向我们解释为什么 已经奇怪地丢失了其元数据 的泛型类型定义 IsGenericTypeDefinition 属性 设置为 false,同时它仍然是泛型类型定义,最后,如果您对其调用 GetGenericTypeDefinition(),您将恢复类型信息。

这很奇怪...

下面的等式将是true:

Type IListType = new List<string>().GetType()
                        .GetInterface("IList`1")
                        .GetGenericTypeDefinition();

// Got interface is "like a generic type definition" since it has
// no type for T generic parameter, and once you call 
// GetGenericTypeDefinition() again, it recovers the lost metadata 
// and the resulting generic type definition equals the one got from
// List<string>!
Type IListType2 = typeof(List<>).GetInterface("IList`1").GetGenericTypeDefinition();

bool y = IListType == IListType2;

IList<T> 的第一个版本是 IList<T> 的实际键入版本,比方说 IList<string>

第二个是 IList<T> 的通用定义,没有 T 的类型。

这使得两个界面不同。不一样,因为第一个是第二个的具体版本。

以下类型都是不同的,没有继承关系:

  • IList<T>
  • IList<int>
  • IList<string>

所有这些都有不同的 Type 对象,因为您可以用它们做不同的事情。后两者是前者的专业化。首先是泛型类型定义(可以通过GetGenericTypeDefinition获得)。

还有一部分要解释。当您说 class List<T> : IList<T> 时,IList<T> 部分 而不是 等于 typeof(IList<>),因为它已经专用于 T。这不再是泛型类型定义。它是一个具体类型,例如IList<int>。它专门用于将其唯一的类型参数绑定到 List<T> 专门针对的 T


LINQPad 实验:

Type bound = new List<string>().GetType().GetInterface("IList`1");
bound.GenericTypeArguments.Single().Dump(); //string


Type bound = typeof(List<>).GetInterface("IList`1");
bound.GenericTypeArguments.Single().Dump(); //"T"
(bound.GenericTypeArguments.Single() == typeof(List<>).GetGenericArguments().Single()).Dump(); //true