C# 泛型详解:从入门到掌握 Generics,写出高复用、高性能代码
在 C# 开发过程中,我们经常会遇到这样的问题:
同样的功能,只是数据类型不同,却需要重复编写大量代码。
例如:
- 一个方法处理 int 类型
- 一个方法处理 string 类型
- 一个方法处理 double 类型
有没有一种方式,可以让代码“一次编写,多种类型复用”?
答案就是:
泛型(Generic)
泛型是 C# 中非常重要的高级特性,也是现代 .NET 开发的基础。
你每天使用的:
List<T> Dictionary<TKey,TValue> IEnumerable<T> Task<T> Func<T> Action<T>全部建立在泛型基础之上。
掌握泛型,是从C# 入门开发者迈向高级开发者的重要一步。
一、什么是 C# 泛型?
1.1 泛型的定义
泛型(Generics)是一种类型参数化技术。
简单来说:
泛型允许我们在定义类、方法、接口时,不提前指定具体的数据类型,而是在使用时再确定类型。
例如:
List<int> List<string> List<double>这里:
T = int T = string T = doubleT就代表一个未知类型。
二、为什么需要泛型?
2.1 没有泛型的问题
假设我们需要打印不同类型的数据:
public void PrintInt(int value) { Console.WriteLine(value); } public void PrintString(string value) { Console.WriteLine(value); } public void PrintDouble(double value) { Console.WriteLine(value); }可以发现:
代码逻辑完全一样。
区别只是:
int string double类型不同。
如果项目中存在几十种类型:
代码维护成本会非常高。
三、使用泛型解决代码重复问题
我们可以改造成:
public void Print<T>(T value) { Console.WriteLine(value); }调用:
Print<int>(100); Print<string>("Hello C#"); Print<double>(3.14);输出:
100 Hello C# 3.14一个方法解决所有类型。
四、泛型方法详解
4.1 泛型方法语法
访问修饰符 返回类型 方法名<T>(参数) { }例如:
public void Show<T>(T value) { Console.WriteLine(value); }其中:
| 部分 | 含义 |
|---|---|
| Show | 方法名称 |
| T | 类型参数 |
| value | 泛型参数 |
五、泛型类型自动推断
C# 编译器非常智能。
例如:
Show(100); Show("C#");编译器自动推断:
第一次:
T = int第二次:
T = string所以通常开发中:
Show(100);比:
Show<int>(100);更加常见。
六、泛型类(Generic Class)
泛型不仅可以用于方法,也可以用于类。
普通类:
public class Student { public int Id { get; set; } }只能保存 int。
如果需要 string:
需要重新创建一个类。
使用泛型类:
public class Student<T> { public T Id { get; set; } }使用:
Student<int> student1 = new Student<int>(); student1.Id = 100; Student<string> student2 = new Student<string>(); student2.Id = "S001";同一个类支持:
int string double DateTime等各种类型。
七、C# 泛型集合(重点)
在实际开发中,泛型集合使用频率最高。
命名空间:
using System.Collections.Generic;1. List<T> 动态数组
List<T> 是最常用集合。
示例:
List<string> names = new List<string>(); names.Add("张三"); names.Add("李四"); foreach(string name in names) { Console.WriteLine(name); }输出:
张三 李四2. Dictionary<TKey,TValue>
字典集合:
保存:
Key + Value例如:
用户ID对应用户名:
Dictionary<int,string> users = new Dictionary<int,string>(); users.Add(1,"Tom"); users.Add(2,"Jack"); Console.WriteLine(users[1]);输出:
Tom3. Queue<T> 队列
特点:
先进先出(FIFO)
例如:
排队买票。
Queue<string> queue = new Queue<string>(); queue.Enqueue("用户A"); queue.Enqueue("用户B"); Console.WriteLine(queue.Dequeue());输出:
用户A4. Stack<T> 栈
特点:
先进后出(LIFO)
例如:
浏览器返回。
Stack<int> stack = new Stack<int>(); stack.Push(10); stack.Push(20); Console.WriteLine(stack.Pop());输出:
20八、泛型接口
接口也支持泛型。
例如:
public interface IRepository<T> { void Add(T entity); T Get(int id); }实现:
public class UserRepository : IRepository<User> { public void Add(User user) { } public User Get(int id) { return new User(); } }优势:
同一个接口支持:
User Order Product Customer等实体。
九、泛型约束(高级重点)
有时候我们需要限制泛型类型。
例如:
只允许引用类型:
class Repository<T> where T : class { }表示:
T必须是:
class类型常见泛型约束
1. 引用类型约束
where T : class2. 值类型约束
where T : struct例如:
Repository<int>3. 无参构造约束
where T : new()要求:
public class User { public User() { } }4. 父类约束
where T : Animal表示:
T必须继承 Animal。
十、泛型和 Object 的区别
很多初学者会问:
为什么不用 object?
例如:
object value = 100;这里发生:
装箱 Boxing
数据:
int ↓ object取出:
int number = (int)value;需要强制转换。
泛型:
List<int>直接保存:
int优势:
| 比较 | object | 泛型 |
|---|---|---|
| 类型安全 | ❌ | ✅ |
| 性能 | 较低 | 高 |
| 需要转换 | 需要 | 不需要 |
| 代码复用 | 一般 | 优秀 |
十一、泛型实际开发应用
1. 数据仓储层
例如:
public class Repository<T> { public void Add(T entity) { Console.WriteLine("保存成功"); } }使用:
Repository<User> userRepository = new Repository<User>(); Repository<Order> orderRepository = new Repository<Order>();一套代码:
支持:
用户 订单 商品 日志十二、C# 中常见泛型类型
.NET大量使用泛型:
| 类型 | 用途 |
|---|---|
| List<T> | 集合 |
| Dictionary<TKey,TValue> | 键值数据 |
| IEnumerable<T> | 数据遍历 |
| Task<T> | 异步返回 |
| Nullable<T> | 可空类型 |
| Func<T> | 函数 |
| Action<T> | 操作委托 |
十三、泛型面试常考问题
1. 什么是泛型?
答:
泛型是一种类型参数化技术,可以提高代码复用性和类型安全性。
2. 泛型相比 object 有什么优势?
答:
主要有:
- 避免装箱拆箱
- 类型安全
- 性能更高
- 减少代码重复
3. 泛型约束有什么作用?
答:
限制泛型类型必须满足一定条件,例如:
- 必须是类
- 必须继承某个类型
- 必须具有无参构造函数
十四、总结
C# 泛型是现代 .NET 开发不可缺少的技术。
通过泛型,我们可以:
✅ 减少重复代码
✅ 提高代码复用能力
✅ 保证类型安全
✅ 提升程序性能
✅ 构建更加灵活的架构
