你是否曾有过这样的感觉——使用了多年的某个功能突然显得……不完整?这就是我一直对扩展方法的感受。它们确实很巧妙,但从未真正融入语言体系。它们就像是戴着整洁语法面具的临时解决方案。
C# 14刚刚通过扩展成员(Extension Members) 解决了这个问题。我现在就要断言:这是整个版本中最好的功能。
让我告诉你为什么。
我从不讨厌扩展方法。它们很有用。但说实话,它们总是让人觉得语言在说:
"嘿,我们知道你想在不修改源码的情况下扩展API,所以这里有一个伪装成更酷东西的静态方法。别看得太仔细。"
而你确实仔细看了,不是吗?
现在有了扩展成员——这是扩展方法一直想要成为的成熟版本。
如果你使用过MVVM模式(WPF、Xamarin、MAUI等),可能已经写过上百次这样的代码:
public static class ObservableExtensions
{
public static ObservableCollection<T> ToObservable<T>(this IEnumerable<T> source)
=> new ObservableCollection<T>(source);
}
它能用,但感觉很别扭;它不像是对现有类型的扩展。现在来看看扩展成员的方式:
public static class EnumerableExtensions
{
extension(IEnumerable<T> collection)
{
public ObservableCollection<T> ToObservable()
=> new ObservableCollection<T>(collection);
}
}
看到区别了吗?没有杂乱代码。没有奇怪的"静态方法假装成真实成员"。感觉就像IEnumerable<T>本身就拥有这个行为。如果你在职业生涯中使用过Swift,可能会对这个功能深有感触,我理解,这简直是🤌(完美)
如果你想掌握这个语法:
public static class NameOfExtensionClass
{
extension(YourType obj)
{
// 方法、属性、运算符,甚至嵌套类型
}
}
是的,你没看错,允许使用属性和运算符。
是否曾想要一种简洁的方法来检查集合是否为空,而不必到处写!collection.Any()或collection.Count == 0?
使用扩展成员:
public static class CollectionExtensions
{
extension(ICollection<T> collection)
{
public bool IsEmpty => this.Count == 0;
}
}
用法:
if (myList.IsEmpty)
{
// 终于以应有的方式阅读代码了
}
这是扩展方法永远无法优雅实现的事情;现在它感觉像是类型系统的自然组成部分。
你甚至可以向不拥有的类型添加静态成员:
public static class DateTimeExtensions
{
extension(DateTime)
{
public static DateTime UnixEpoch => new DateTime(1970, 1, 1);
}
}
有一整个静态扩展方法库?可以这样迁移:
之前:
public static class DateTimeExtensions
{
public static bool IsWeekend(this DateTime date)
=> date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
}
之后:
public static class DateTimeExtensions
{
extension(DateTime dateTime)
{
public bool IsWeekend => dateTime.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
}
}
更简洁。更易发现。更易维护。
对于好奇的读者,这里简单说明:
想让我深入探讨机制吗?在评论区留言,我可能会专门写一篇文章。
它们是新功能,所以:
但说实话?相对于我们获得的功能,这些都是次要的权衡。
扩展成员终于让扩展感觉像是一等公民。它们让API自然演进。它们减少了代码杂乱。它们让代码读起来就像原本就是这样设计的。
这不仅仅是语法糖。这是让我对重构旧代码库感到兴奋的功能——我不常这么说。
扩展成员不仅仅是一个闪亮的新语法。它们是一次课程修正。它们完成了扩展方法开始的工作。更简洁的API、可发现的功能、隐藏在随机命名空间中的工具类更少,这一切都感觉……恰到好处。
这就是为什么这是我最喜欢的C# 14功能。它不会让你重新思考如何编写部分代码,而是让你重新思考如何设计所有代码。
现在轮到你了:你首先会将什么重构为扩展类型?你会全力以赴迁移所有辅助类,还是用一个杀手级属性来试水?在评论区告诉我,我想看看你的想法。
如果我遗漏了什么,请在评论区补充。另外,如果你发现博客中有任何不正确的地方,也请在评论区指正。