C# 扩展方法
2022 年 4 月 21 日 · 1 分钟读完
微软文档的说法:「扩展方法使你能够向现有类型添加方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。」也就是说,可以给 string、int、DataRow、DataTable 这些改不了源码的类型加方法。
三个要素
- 方法必须是静态方法;
- 方法必须放在静态类(非嵌套、非泛型)里;
- 第一个参数以
this开头,this后面的类型就是要扩展的类型。
例子
一个学生类和一个学生列表类:
class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
class StudentList
{
List<Student> mStudentList = new List<Student>(); // 私有
public int StudentsCount => mStudentList.Count;
public void AddStudent(string name, int age)
{
mStudentList.Add(new Student { Name = name, Age = age });
}
}
给 StudentList 写扩展:
static class StudentListEx
{
// 扩展方法拿到的只是一个普通实例,访问不到私有成员
// public static int SumOfStudentsAge(this StudentList studentList)
// {
// foreach (var item in studentList.mStudentList) // 编译错误
// }
public static void WriteLineStudentsCount(this StudentList studentList)
{
Console.WriteLine("学生个数:" + studentList.StudentsCount);
}
// 和实例方法签名完全相同:永远不会被调用
public static void AddStudent(this StudentList studentList, string name, int age)
{
Console.WriteLine("调用扩展方法");
}
// 参数不同:相当于新增了一个重载
public static void AddStudent(this StudentList studentList, Student student)
{
Console.WriteLine("调用扩展方法2");
studentList.AddStudent(student.Name, student.Age);
}
}
调用:
var studentList = new StudentList();
studentList.AddStudent("小明", 10); // 实例方法
studentList.AddStudent(new Student { Name = "小胡椒", Age = 11 }); // 扩展方法 2
studentList.WriteLineStudentsCount(); // 学生个数:2

规则总结
- 扩展方法只能访问类型的公开成员,访问不到
private。 - 和实例方法签名相同时,实例方法优先,扩展方法不会被调用(编译器也不报错,要小心)。
- 只有被扩展类型(及其子类、实现类)的变量能用。扩展父类,子类对象也能用;扩展接口,所有实现类都能用。
- 使用前要
using扩展类所在的命名空间。 - 本质是语法糖:
studentList.WriteLineStudentsCount()会被编译成StudentListEx.WriteLineStudentsCount(studentList),所以对 null 调用扩展方法不会立刻抛异常,异常发生在方法内部访问成员时。
延伸
- LINQ 就是一大堆扩展方法:
Where、Select、OrderBy都定义在System.Linq.Enumerable这个静态类里,扩展的是IEnumerable<T>。这就是为什么不写using System.Linq;就点不出它们。 - 扩展接口最有用:给
IEnumerable<T>写一个ForEach,给Control写一个线程安全的InvokeIfRequired,所有实现类都能直接用。
public static class ControlExtensions
{
// WinForm 跨线程更新界面的常用写法
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired) control.Invoke(action);
else action();
}
}
label1.InvokeIfRequired(() => label1.Text = "完成");
- ASP.NET Core 的
services.AddXxx()也是扩展方法,各个库通过扩展IServiceCollection来提供注册入口。 - C# 14 的扩展成员:新语法
extension(StudentList list) { ... }块里除了方法,还能写扩展属性和静态扩展成员。旧写法继续可用。
原文首发于 blog.csdn.net