解释泛型在 C# 中的工作原理
泛型是在 C# 2.0 版中添加的,是该语言中最重要的概念之一。它们使您能够编写可重用、高性能且在编译时类型安全的代码。使用泛型,您可以在代码中使用类型,而无需事先了解它。
泛型在 .NET 中的许多地方都有使用,包括集合、委托和异步代码。使用泛型,您无需事先知道集合的大小,并且可以将泛型与任何元素类型一起使用,甚至是特定于您的代码的自定义数据类型。C# 为泛型类型(类、接口等)和泛型方法提供支持。
在泛型中,您有类型参数和类型参数。这类似于具有参数的方法,您可以将参数传递给方法。
泛型类型
声明泛型类型的语法由类型名称后面的尖括号中的类型参数组成。例如,Locator<T> 在下面的示例中是一个泛型类。
public class Locator<T> { }
要创建 Locator<T> 的实例,请使用 new 关键字,后跟类的名称。但是,您不是使用 T,而是指定要作为参数传递的实际类型。以下示例将字符串类型作为参数传递。
var stringLocator = new Locator<string>();
您可以在类方法上使用类型参数 (T),如下例所示。
public class Locator<T>{ public IList<T> Items { get; set; } public T Locate(int index){ return Items[index]; } } var stringLocator = new Locator<string>(); string item = stringLocator.Locate(2);
泛型的另一个好处是编辑器提供的 IntelliSense。当您在 Visual Studio 或 VS Code 中输入 stringLocator.Locate(4) 并将鼠标悬停在方法名称上时,它会显示它返回的是字符串而不是 T。如果您尝试将结果分配给字符串以外的任何类型,编译器将引发错误。例如,
// 错误:无法将类型 'string' 隐式转换为 'int' [c-sharp]csharp(CS0029) int item = stringLocator.Locate(2);
泛型类型在从泛型基类型或泛型接口继承时可以使用类型参数作为类型参数。泛型 LinkedList<T> 类型实现泛型 IEnumerable<T>接口,以及其他。
public class LinkedList<T> : IEnumerable<T>
泛型方法
泛型方法只是一种声明类型参数的方法,您可以在方法内部使用它,并将其用作参数和返回类型。在下面的示例中,Swap<T> 是一种泛型方法,它接受两个类型为 T 的参数并返回 T 的实例。
public class Swapper{ public T Swap<T>(T first, T second){ T temp = first; first = second; second = temp; return temp; } }
与泛型类型一样,当您调用泛型方法时,它将返回一个强类型变量。
var swapper = new Swapper(); int result = swapper.Swap<int>(5, 3);
可以有多个泛型参数。System.Collections.Generic 命名空间中的 Dictionary 类有两个类型参数,分别用于键和值。
public class Dictionary<TKey, TValue>
最后,了解什么可以是泛型很重要。对于类型,除了枚举之外,所有内容都可以是泛型。包括 −
- 类
- 结构
- 接口
- 委托
对于类型成员,只有方法和嵌套类型可以是泛型的。以下成员不能是泛型的 −
- Fields
- Properties
- Indexers
- Constructors
- Events
- Finalizers
示例
using System; using System.Collections.Generic; class Program{ static void Main(){ var stringLocator = new Locator<string>(){ Items = new string[] { "JavaScript", "CSharp", "Golang" } }; string item = stringLocator.Locate(1); Console.WriteLine(item); // CSharp var swapper = new Swapper(); int a = 5, b = 3; int result = swapper.Swap<int>(ref a, ref b); Console.WriteLine($"a = {a}, b = {b}"); } } public class Locator<T>{ public IList<T> Items { get; set; } public T Locate(int index){ return Items[index]; } } public class Swapper{ public T Swap<T>(ref T first, ref T second){ T temp = first; first = second; second = temp; return temp; } }
输出
CSharp a = 3, b = 5