C# 泛型
# 一、泛型方法中 <T> 的含义与作用
1. **<T> 表示类型参数**
T 是一个**占位符**,代表**任意一种数据类型**。
在方法定义时不确定具体类型,调用时才确定。
2. **T 可以是任意类型**
包括:
- 值类型:int、double、bool、char、struct
- 引用类型:string、类、数组、接口
、、、csharp
using System;
class Program
{
// 定义泛型方法,<T> 为类型占位符
public static void ShowInfo<T>(T data)
{
Console.WriteLine($"数据值:{data},数据类型:{typeof(T)}");
}
static void Main()
{
// 1. 值类型调用
ShowInfo<int>(100);
ShowInfo<double>(3.14);
ShowInfo<bool>(true);
ShowInfo<char>('A');
// 2. 引用类型调用
ShowInfo<string>("测试字符串");
ShowInfo<object>(new object());
// 编译器可自动推断类型,省略 <T>
ShowInfo(666);
ShowInfo("自动推断类型");
Console.ReadKey();
}
}
3. **类型推导机制**
调用泛型方法时,**编译器会根据传入参数自动推导 T 的类型**,
不需要手动写 <int> / <string>。
4. **泛型的核心优势**
一套代码,支持所有类型,**避免重复代码、避免类型转换、提高安全性**。
、、、csharp
using System;
class GenericDemo
{
// 泛型方法
public static void PrintData<T>(T value)
{
Console.WriteLine($"值:{value},类型:{typeof(T).Name}");
}
static void Main()
{
// 1. 类型推导:省略 <类型>,编译器自动识别
PrintData(100); // 自动推导 T = int
PrintData(3.14); // 自动推导 T = double
PrintData("Hello"); // 自动推导 T = string
PrintData(true); // 自动推导 T = bool
// 也可以手动指定类型,两种写法都合法
PrintData<string>("手动指定字符串类型");
// 对比:不用泛型,需要写多个重载方法,代码冗余
// public static void Print(int a){}
// public static void Print(double a){}
// public static void Print(string a){}
Console.ReadKey();
}
}
# 二、泛型的重要限制:未约束 T 不能直接使用 + 运算符
1. **未约束的泛型 T,编译器不知道它是什么类型**
不知道是否支持 `+`、`-`、`*`、`/` 等运算符。
2. **直接对 T 类型变量做数学运算会编译报错**
因为编译器无法验证运算是否合法。
3. **解决方式**
- 使用**泛型约束**
- 使用动态类型 dynamic
- 使用运算符重载或第三方库辅助
4. **核心结论(必背)**
**无约束的泛型 T 不能直接进行算术运算。**
、、、csharp
using System;
class Program
{
// 无约束泛型方法
public static void Calc<T>(T a, T b)
{
// 编译报错:无约束 T 不能直接使用 + 运算符
// T result = a + b;
}
// 方式1:使用 dynamic 绕过限制
public static void AddByDynamic<T>(T a, T b)
{
dynamic d1 = a;
dynamic d2 = b;
var res = d1 + d2;
Console.WriteLine($"运算结果:{res}");
}
static void Main()
{
AddByDynamic(10, 20);
AddByDynamic(3.14, 2.86);
Console.ReadKey();
}
}
# 三、方法重载优先级:固定类型方法 > 泛型方法
1. **同一个类中,普通方法(固定类型)与泛型方法可构成重载**
2. **重载匹配规则(考试必考)**
**精确匹配的普通方法 > 泛型方法**
传入 int → 优先调用 int 方法
传入 string → 优先调用 string 方法
无精确匹配时,才会调用泛型方法。
3. **为什么?**
普通方法是**明确类型**,泛型是**通用类型**,
编译器永远优先选择**最具体、最精确**的方法。
4. **必背结论**
**固定类型方法优先级 > 泛型方法。**
、、、csharp
using System;
class Test
{
// 普通 int 类型方法
public static void Show(int num)
{
Console.WriteLine("调用 int 普通方法:" + num);
}
// 普通 string 类型方法
public static void Show(string str)
{
Console.WriteLine("调用 string 普通方法:" + str);
}
// 泛型方法
public static void Show<T>(T data)
{
Console.WriteLine($"调用泛型方法,类型:{typeof(T).Name},值:{data}");
}
static void Main()
{
// 1. 精确匹配,优先执行对应普通方法
Show(100);
Show("C#");
// 2. 无对应普通方法,才会调用泛型方法
Show(3.14);
Show(true);
Console.ReadKey();
}
}
# 四、泛型类继承普通类(非泛型类)的规则
1. **泛型类可以继承非泛型(普通)类**
语法:`class GenericClass<T> : NormalClass`
2. **继承后,父类成员完全可用**
子类可以直接访问父类的字段、属性、方法。
3. **泛型参数 T 只作用于当前泛型类,不影响父类**
父类是普通类,与 T 无关。
T 只在子类内部生效。
4. **语法合法、使用广泛**
常用于:通用工具类、通用管理类、通用模型。
5. **核心结论**
**泛型类可以正常继承非泛型类,继承关系不受泛型影响。**
、、、csharp
using System;
// 非泛型父类(普通类)
class ParentClass
{
public string Name { get; set; }
public void SayHello()
{
Console.WriteLine("来自普通父类的方法:Hello");
}
}
// 泛型子类 继承 普通父类
class ChildClass<T> : ParentClass
{
// 泛型类独有的泛型成员
public T Data { get; set; }
public void ShowData()
{
Console.WriteLine($"泛型数据:{Data},类型:{typeof(T).Name}");
}
}
class Program
{
static void Main()
{
// 使用 int 作为泛型参数
ChildClass<int> obj1 = new ChildClass<int>();
// 调用父类属性和方法
obj1.Name = "测试1";
obj1.SayHello();
// 调用自身泛型成员
obj1.Data = 100;
obj1.ShowData();
Console.WriteLine("------------------");
// 使用 string 作为泛型参数
ChildClass<string> obj2 = new ChildClass<string>();
obj2.Name = "测试2";
obj2.SayHello();
obj2.Data = "泛型字符串";
obj2.ShowData();
Console.ReadKey();
}
}
# 五、四条知识点 终极精简背诵版(最适合记忆)
1. **<T> = 类型参数,调用时自动推导,支持任意类型。**
2. **无约束 T 不能直接运算,因为编译器不知道类型。**
3. **方法重载:普通固定类型方法 优先于 泛型方法。**
4. **泛型类可继承普通类,父类不受 T 影响,子类正常使用父类成员。**
