文章目录
- 前言
- 一、泛型的基本概念与实例展示
- 二、泛型的特性与优势
- 三、泛型方法
- 四、泛型委托
前言
泛型(Generic)允许将类或方法中编程元素的数据类型规范进行延迟编写,直到在程序实际使用这些类或方法的时候再去确定具体的数据类型。
一、泛型的基本概念与实例展示
通过使用数据类型的替代参数来编写类或方法的规范,当编译器遇到类的构造函数被调用或者方法被执行时,它会依据实际传入的数据类型自动生成相应的代码来妥善处理该类型的数据。以下这个简单且直观的实例有助于我们深入理解这一概念:
using System;
using System.Collections.Generic;namespace GenericApplication
{// 定义泛型类 MyGenericArray<T>,这里的 T 就是类型参数,代表之后会确定的具体数据类型public class MyGenericArray<T>{private T[] array;// 构造函数,根据传入的大小创建对应类型的数组,注意这里数组类型是 T[],由类型参数决定public MyGenericArray(int size){array = new T[size + 1];}// 获取指定索引位置元素的方法,返回值类型也是 T,与数组元素类型一致public T getItem(int index){return array[index];}// 设置指定索引位置元素值的方法,接受的参数类型为 Tpublic void setItem(int index, T value){array[index] = value;}}class Tester{static void Main(string[] args){// 声明一个整型数组,此时将类型参数 T 指定为 int,创建了能处理整型数据的 MyGenericArray<int> 实例MyGenericArray<int> intArray = new MyGenericArray<int>(5);// 设置值,通过循环为整型数组元素赋值for (int c = 0; c < 5; c++){intArray.setItem(c, c * 5);}// 获取值,循环获取并输出整型数组元素的值for (int c = 0; c < 5; c++){Console.Write(intArray.getItem(c) + " ");}Console.WriteLine();// 声明一个字符数组,将类型参数 T 确定为 char,创建用于处理字符数据的 MyGenericArray<char> 实例MyGenericArray<char> charArray = new MyGenericArray<char>(5);// 设置值,按照一定规则为字符数组元素赋值for (int c = 0; c < 5; c++){charArray.setItem(c, (char)(c + 97));}// 获取值,循环获取并输出字符数组元素的值for (int c = 0; c < 5; c++){Console.Write(charArray.getItem(c) + " ");}Console.WriteLine();Console.ReadKey();}}
}
当上述代码被编译和执行时,我们可以看到它先是对整型数组进行操作,输出了 0 5 10 15 20,随后对字符数组进行处理,输出了 a b c d e,完美展示了同一个泛型类如何依据不同的数据类型参数,灵活地处理各种类型的数据,体现了泛型的强大通用性。
二、泛型的特性与优势
- 代码重用与类型安全保障
泛型是一种能够极大限度地重用代码的技术手段。以往编写针对不同数据类型但功能相似的代码时,往往需要重复编写大量逻辑相似的代码,而泛型允许我们编写通用的逻辑框架,通过传入不同类型参数就能适用于多种类型的数据处理场景,避免了代码的冗余。同时,泛型还能有效地保护类型的安全。在编译阶段,编译器会基于泛型所确定的数据类型进行严格的类型检查,确保传入和使用的数据类型都是符合预期的,防止出现类型不匹配等错误,大大提高了程序的稳定性和健壮性。 - 泛型集合类的应用
.NET 框架类库在 System.Collections.Generic 命名空间中包含了众多新的泛型集合类。这些泛型集合类相较于 System.Collections 中的传统集合类有着显著优势,比如 List、Dictionary<TKey, TValue> 等。它们能够根据实际需求存储不同类型的数据,并且在进行元素操作、遍历等过程中,通过泛型机制保障类型安全,提高了集合操作的便利性和可靠性。我们可以使用这些泛型集合类来替代传统的非泛型集合类,以获得更好的编程体验和代码性能。 - 多样化的泛型元素创建
开发者不仅可以创建泛型类,还能够创建自己的泛型接口、泛型方法、泛型事件以及泛型委托等。例如,创建泛型接口可以定义一组通用的行为规范,让不同类型的类只要实现该接口并遵循其规范就能共享相同的接口调用逻辑;泛型方法则可以针对具体的某个操作逻辑,使其能适应多种数据类型的输入和处理需求;泛型事件能在事件触发和处理机制中融入泛型的灵活性;泛型委托更是可以依据不同类型参数来代表不同类型的方法签名,实现灵活的方法调用委托机制。 - 泛型类的约束功能
我们可以对泛型类进行约束,以此来访问特定数据类型的方法。通过添加约束条件,能够限定泛型类型参数必须满足某些要求,比如继承自某个特定类或者实现了某个接口等,这样在泛型类的内部代码中就能安全地调用那些满足约束条件的数据类型所特有的方法,进一步丰富了泛型在不同业务场景下的应用灵活性,使其能够更精准地适配各种复杂的编程需求。 - 运行时反射获取类型信息
关于泛型数据类型中使用的类型的信息,在程序运行时可以通过使用反射机制来获取。反射允许程序在运行阶段动态地获取类型的相关信息,包括泛型类型参数具体是什么类型等内容,这为一些需要动态处理不同类型数据或者根据运行时情况调整逻辑的高级编程场景提供了有力支持,例如动态创建泛型对象、根据类型信息调用不同的泛型方法等操作都可以借助反射来实现。
三、泛型方法
在前面介绍的实例中,我们已经见识到了泛型类的使用,其实我们还可以通过类型参数来声明泛型方法。下面这个程序就很好地说明了泛型方法这一概念:
using System;
using System.Collections.Generic;namespace GenericMethodAppl
{class Program{// 定义泛型方法 Swap<T>,用于交换两个同类型的变量的值,T 为类型参数static void Swap<T>(ref T lhs, ref T rhs){T temp;temp = lhs;lhs = rhs;rhs = temp;}static void Main(string[] args){int a, b;char c, d;a = 10;b = 20;c = 'I';d = 'V';// 在交换之前显示值,输出初始的整型和字符型变量的值Console.WriteLine("Int values before calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values before calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);// 调用 swap 方法,分别传入整型和字符型的引用变量,实现对应类型变量值的交换Swap<int>(ref a, ref b);Swap<char>(ref c, ref d);// 在交换之后显示值,输出交换后的整型和字符型变量的值Console.WriteLine("Int values after calling swap:");Console.WriteLine("a = {0}, b = {1}", a, b);Console.WriteLine("Char values after calling swap:");Console.WriteLine("c = {0}, d = {1}", c, d);Console.ReadKey();}}
}
示例中,Swap 方法通过类型参数 T 定义,可以接受任意相同类型的两个引用变量,然后在方法内部实现它们值的交换。无论是整型变量 a 和 b,还是字符型变量 c 和 d,都能通过调用这个泛型方法实现正确的交换操作,充分体现了泛型方法对不同数据类型的通用性,当代码被编译和执行后,会按照预期输出交换前后的变量值,展示了泛型方法的实际效果。
四、泛型委托
泛型委托同样是泛型特性在 C# 中的重要应用形式之一,我们可以通过类型参数来定义泛型委托,使其能够代表不同类型的方法签名,进而实现灵活的方法调用委托机制。例如,以下是一个简单的泛型委托定义示例:
delegate T NumberChanger<T>(T n);
在这个定义中,NumberChanger 就是一个泛型委托,它接受一个类型为 T 的参数,并返回类型同样为 T 的结果,通过不同的类型参数 T 的代入,就能代表不同类型输入输出要求的方法。下面通过一个完整的实例来演示泛型委托的使用:
using System;
using System.Collections.Generic;delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}static void Main(string[] args){// 创建委托实例,将 AddNum 方法关联到 NumberChanger<int> 委托类型上,此时委托可用于调用 AddNum 方法NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);// 同样创建委托实例,将 MultNum 方法关联到委托类型上NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);// 使用委托对象调用方法,通过 nc1 委托调用 AddNum 方法并传入参数 25nc1(25);Console.WriteLine("Value of Num: {0}", getNum());// 通过 nc2 委托调用 MultNum 方法并传入参数 5nc2(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}
实例中,首先定义了 NumberChanger 泛型委托,然后创建了两个该委托类型的实例,分别关联了 AddNum 和 MultNum 两个方法,之后通过委托实例来调用相应的方法,实现了对不同方法的灵活调用和逻辑整合。当代码被编译和执行时,会按照方法的逻辑执行相应运算,并输出每次操作后的 num 值,展示了泛型委托如何依据不同的方法实现和参数类型,在程序中起到灵活调配方法调用的重要作用。