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