在.NET中,“开放式泛型类型”是什么?

148

我正在学习 Asp.Net MVC 课程,并且了解到一个方法要作为控制器的操作,需要满足以下条件:

  • 它不能有“开放式泛型类型”

我对泛型有一定的了解并在某种程度上使用它们,但是:

  • .Net 中的开放式泛型类型是什么?
  • 是否存在封闭式泛型类型?
  • 开放式泛型类型 不常使用这个术语,有哪些术语会被使用或混淆?
4个回答

239
C#语言定义开放类型为类型参数或使用未知类型参数定义的泛型类型:
所有类型都可以被分类为开放类型或封闭类型。开放类型是涉及类型参数的类型,更具体地说:
- 类型参数定义开放类型。 - 如果其元素类型是开放类型,则数组类型是开放类型。 - 构造类型仅在其一个或多个类型参数是开放类型时才是开放类型。如果一个构造嵌套类型或其包含类型的类型参数是开放类型,则该类型也是开放类型。
封闭类型是不是开放类型的类型。
因此,T、List、Dictionary和Dictionary都是开放类型(T和U是类型参数),而List和Dictionary是封闭类型。
还有一个相关概念:未绑定的泛型类型是具有未指定类型参数的泛型类型。未绑定类型不能用于除typeof()之外的表达式,也无法实例化它或调用其方法。例如,List<>和Dictionary<,>是未绑定类型。
为了澄清开放类型和未绑定类型之间微妙的区别:
class Program {
   static void Main() { Test<int>(); }
   static void Test<T>() {
      Console.WriteLine(typeof(List<T>)); // Print out the type name
   }
}

如果您运行此代码片段,它将打印出:
System.Collections.Generic.List`1[System.Int32]

这是 List<int> 的CLR名称。在运行时,类型参数为 System.Int32 是明确的。这使得 List<T> 成为一个绑定开放类型。

在运行时,您可以使用反射将类型参数绑定到未指定的未绑定泛型类型的类型参数,方法是使用 Type.MakeGenericType 方法

Type unboundGenericList = typeof(List<>);
Type listOfInt = unboundGenericList.MakeGenericType(typeof(int));
if (listOfInt == typeof(List<int>))
     Console.WriteLine("Constructed a List<int> type.");

你可以使用Type.IsGenericTypeDefinition属性检查类型是否为未绑定的泛型类型(泛型类型定义)。从这种类型中,你可以构造出具有限制的类型。
Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); // True
Console.WriteLine(typeof(Dictionary<int,int>).IsGenericTypeDefinition); // False

在运行时获取构造类型的非限定类型,您可以使用Type.GetGenericTypeDefinition方法。请注意保留HTML标签。
Type listOfInt = typeof(List<int>);
Type list = listOfInt.GetGenericTypeDefinition(); // == typeof(List<>)

请注意,对于泛型类型,您可以拥有完全未绑定的类型定义或完全绑定的定义。您不能绑定一些类型参数并保留其他未绑定。例如,您不能拥有Dictionary<int,>Dictionary<,string>

12
+1 很棒的信息 - 今天我学到了新知识。我知道 List<> 是一个泛型类型,但现在我知道了正确的技术术语。 - IAbstract
3
实际上,您可以通过提供一个自身是开放式泛型类型的类型参数来部分地关闭通用类型。然而,这是一条死路。框架不正式承认这种部分状态,将其视为既不是关闭也不是开放,因此不允许您对其执行任何有用的操作。 - Chris Ammerman
1
很好的解释了开放类型和未绑定类型之间的区别 - 以前不知道! - nawfal
2
我同意@ChrisAmmerman的观点。例如,你声称我们无法拥有的类型Dictionary<int,>,实际上可以通过typeof(Dictionary<,>).MakeGenericType(typeof(int), typeof(Dictionary<,>).GetGenericArguments()[1])来构造。但也许你只是想说C#不允许使用语法Dictionary<int,>,所以我们必须像我在这里做的那样手动创建它。 - Jeppe Stig Nielsen
IsGenericType文档页面上,在示例部分的C#代码中,他们以更优雅的方式考虑了一个类型Base`2[System.String,V],其中一个类型参数被替换为具体类型,另一个类型参数被替换为参数V - Jeppe Stig Nielsen

18
一个“开放的泛型类型”就是一个还没有指定其类型的泛型类型(例如,CargoCrate<T>)。一旦分配了具体的类型(例如,CargoCrate<Widget>),它就变成了“关闭”的。
例如,假设你有这样的东西:
public class Basket<T> {
  T[] basketItems;
}

public class PicnicBlanket<T> {
  Basket<T> picnicBasket;   // Open type here. We don't know what T is.
}

                                 // Closed type here: T is Food.
public class ParkPicnicBlanket : PicnicBlanket<Food> {
}

这里,picnicBasket 的类型是开放的:没有为 T 分配任何值。当您使用特定类型创建一个具体的 PicnicBlanket 时 -- 例如,通过编写 PicnicBlanket<Food> p = new PicnicBlanket<Food>() -- 我们现在称其为 封闭的


14

补充一点:

Dictionary<string, T> (或更准确地说是Dictionary<string,>)仍然是一个开放类型。

例如:

void Foo<T>(Dictionary<string,T> dic) { ... }

3
查阅规范后,我发现你是对的。我原以为“泛型类型定义”和规范中的“开放类型”是同一个东西,但是显然,规范将类型参数和List<T>定义为开放类型,而List<>定义为未绑定类型。澄清我的答案。+1 - Mehrdad Afshari

11

有三种泛型类型。简而言之,在这个(简化的)声明中:

public class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
  • Dictionary<TKey, TValue> 是一个 无限制的泛型类型

  • KeyValuePair<TKey, TValue> 在这种情况下是一个 开放构造的泛型类型。它具有一些类型参数,但它们已在其他地方(在本例中为 Dictionary)中定义。

  • Dictionary<string, int> 将是一个 封闭构造的泛型类型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接