扩展方法必须定义在静态类中,且方法本身为static,首参数用this修饰目标类型,需通过using引入命名空间才能调用。

扩展方法必须定义在静态类中
扩展方法本质是编译器特性,不是真正的“给类型加方法”,所以 C# 强制要求:所有扩展方法必须声明在 static 类里,且方法本身也必须是 static。如果漏掉任一 static,编译器会报错 CS1106: Extension method must be defined in a non-generic static class。
- 类名可以任意,比如
StringExtensions、EnumerableHelpers,但必须加static修饰符 - 方法第一个参数用
this修饰,类型即为要扩展的目标类型(如this string s表示扩展string) - 不能是泛型类的嵌套类型(例如
MyClass不合法).Extensions
扩展方法的第一个参数必须带 this 修饰符
this 是扩展方法的语法标记,不是可选的。它告诉编译器“这个方法将作为目标类型的实例方法被调用”。没有 this,就是普通静态方法,无法用点号链式调用。
public static class DateTimeExtensions
{
// ✅ 正确:this DateTime dt 表示扩展 DateTime 类型
public static string ToShortChineseDate(this DateTime dt)
{
return $"{dt.Year}年{dt.Month}月{dt.Day}日";
}
// ❌ 错误:缺少 this,编译通过但无法作为扩展方法使用
public static string ToShortChineseDate(DateTime dt) { ... }}
-
this参数类型可以是具体类型(string)、接口(IEnumerable)、委托或泛型约束类型 - 不能是
dynamic或指针类型(如int*) - 扩展
Nullable(如int?)时,写成this int? value即可,无需特殊处理
扩展方法的调用依赖 using 指令和作用域可见性
即使定义了扩展方法,如果当前文件没引入对应命名空间,或者方法所在类不可见(比如 internal 类在另一个程序集),IDE 就不会显示智能提示,调用也会失败,报错 CS1061: 'xxx' does not contain a definition for 'yyy'。
- 确保扩展方法所在的命名空间已用
using引入(如using MyExtensions;) - 如果扩展方法类是
internal,只能在同一程序集内使用;跨程序集需设为public - 当多个命名空间含同名扩展方法时,C# 按 using 声明顺序和重载匹配规则选择,可能引发歧义或静默覆盖
- 扩展方法永远低于类型自身定义的方法优先级——哪怕签名完全一致,实例方法也一定胜出
泛型扩展方法要注意类型约束和推导限制
给泛型类型写扩展方法很常见(比如给所有 IEnumerable 加 CountNotNull()),但类型参数不能自动推导全部场景,尤其涉及协变/逆变或复杂约束时。
public static class EnumerableExtensions
{
// ✅ 支持 T 为任何引用类型
public static int CountNotNull(this IEnumerable source) where T : class
{
return source?.Count(x => x != null) ?? 0;
}
// ⚠️ 注意:以下调用可能失败
// var list = new Listzuojiankuohaophpcnstringyoujiankuohaophpcn();
// list.CountNotNull(); // ✅ 可推导 T = string
// Enumerable.Emptyzuojiankuohaophpcnobjectyoujiankuohaophpcn().CountNotNull(); // ✅
// Enumerable.Range(1,3).CountNotNull(); // ❌ int 不满足 class 约束
}
- 泛型约束(
where T : class、where T : new() 等)必须显式写出,否则编译不通过
- 调用时若无法从参数推导出泛型实参,需手动指定,如
source.CountNotNull()
- 避免过度泛化:扩展
object 或无约束泛型 T 很容易污染 IntelliSense,降低可读性
扩展方法看似简单,但真正用稳的关键在于:静态类 + this 参数 + 命名空间可见性 + 类型约束意识。最容易忽略的是作用域问题——写了半天发现调用处没 using,或者类被默认 internal 锁死。









