为什么不支持C#静态类扩展方法?
为什么不支持C#静态类扩展方法?
C#中不支持对静态类添加扩展方法的原因是因为这没有任何意义。引入扩展方法的主要原因不是为了扩展您没有所有权的类,而是为了减少方法嵌套的层次结构。例如:
Enumerable.Select(Enumerable.Where(arr, i => i & 1 == 0), i => i*i); //不是最好的代码 arr.Where(i => i & 1 == 0).Select(i => i*i); //哇,我明白了!这是奇数的平方
这就是为什么没有必要为静态类添加扩展方法的原因。
我曾多次遇到想要向静态类添加方法的情况。例如,我想添加`Path.Combine(params string[])`和`Path.GetMimeType(string path)`方法。
对于这种情况,普通的静态方法应该就足够了。并没有太大必要假装这些方法属于`System.IO.Path`类。我甚至坚持认为这是一个不好的决定。如果需要这样的功能,可以创建自己简单的静态类。
正确的答案。我还想补充一点,您可以使用面向对象设计原则,例如聚合和/或其他设计模式,比如装饰器模式,以实现这一点,并且仍然提供非常可读和可维护的代码。
Snowbear:对于普通的扩展方法也可以这样说,但它们确实非常有用。
为什么不支持C#静态类扩展方法?
在阅读了答案以及一些相关问题后,我在这里总结了对这个问题的理解。
扩展方法的工作原理
首先,重要的是要意识到扩展方法只是静态方法的一种语法糖。
// 假设你有一个扩展方法如下:
class Extensions
{
public static void Extend(this SomeClass foo) {}
}
// 这是如何调用它的
SomeClass myClass;
myClass.Extend();
// 编译器将其转换为这样:
Extensions.Extend(myClass);
实际上,该方法并不成为类的一部分。这就是为什么你不能从扩展方法中访问私有成员的原因。扩展方法只改变了C#的语法,不违反面向对象编程的可访问性概念。实际上,如果你编写一个与普通静态方法执行相同操作的扩展方法,然后反编译MSIL,它们是完全相同的。
扩展方法的存在原因
那么,如果它们并没有添加实际功能,为什么要有扩展方法呢?答案是LINQ。
// 使用LINQ使得代码易于阅读
array.Where(i => i%2 == 0).Select(i => i*i);
// 如果没有扩展方法,我们将不得不这样写
Enumerable.Select(Enumerable.Where(array, i => i%2 == 0), i => i*i);
从某种意义上说,所有的LINQ只是语法糖,因为它可以用笨拙的非LINQ方式编写。显然,C#团队认为通过LINQ获得的可读性是值得的,但这引发了一个问题,“为什么他们止步于此?”
为什么不支持其他类型的扩展?
C#编译器开发人员之一Eric Lippert在一篇博文中描述了C# 3的一个重要部分是为LINQ创建所有必要的结构:“隐式类型本地变量,匿名类型,lambda表达式,扩展方法,对象和集合初始化,查询表达式,表达式树,改进的方法类型推断”。因为C#团队是2008年.NET版本中资源最有限的团队,所以没有包括不严格需要用于LINQ的其他类型的扩展。
团队确实考虑在C# 4中实现扩展属性,并且实际上编写了一个工作原型,但当他们发现它不能像实现的那样对WPF团队进行启用时(这是该功能的动力之一),就放弃了。Eric Lipper后来说,他们确实考虑过为静态类实现扩展方法,但无法证明实际的好处超过了实现、测试和维护的成本。
解决方法
有一种方法可以编写接近的扩展方法:
public static TResult DoSomething
{
// 使用System.Reflection访问静态方法
return default(TResult);
}
// 这样可以工作,但效果不好
typeof(Math).DoSomething();
typeof(Convert).DoSomething();
但这相当丑陋。它需要使用反射,并且不能支持任何类型的智能类型化,因为任何Type都可以调用它,而这可能不是预期的功能。
为什么不支持C#静态类扩展方法?
C#团队在实现这种功能时可能会遇到一些技术上的困难。但是,正如我经常强调的那样,我不必为不实现某个特性提供理由。特性并不廉价;它们非常昂贵,不仅必须证明自己的成本,还必须证明不实现其他可能具备的一百个特性所带来的机会成本。我们必须向利益相关者证明特性的成本,但我们无需为不符合标准的特性节省时间和精力而提供理由。
特别是,这个提议的特性对LINQ并没有任何作用;扩展方法是为了使LINQ工作而添加的。任何与使LINQ工作无关的东西都很难被纳入C# 3.0;我们的日程安排上有很多工作要做,但时间不多(我对自动属性能被纳入其中感到惊讶)。在甚至设计这个不必要的特性之前就削减了它,节省了大量的时间和精力,这些时间和精力都花在了使LINQ工作的其他事情上。
简而言之:这个提议的特性从未达到我们对成本效益的标准,我们一直有更重要的特性可以利用我们有限的时间和精力来实现。
这对于C# 3来说是有道理的。你提到你在C# 4中为扩展属性制作了原型,但后来取消了这个特性。可以认为其他类型的扩展(比如静态类方法)由于资源预算的原因在C# 4中也没有得到批准吗?
是的,我们有关于“扩展一切”的设计想法 - 构造函数、事件等等 - 但如果我们甚至不能容纳扩展属性,那么就没有太大的意义了。它们很好,但其附加值不足以证明成本。
是什么驱动了C#语言的发展:已实现的特性还是规范?它们似乎是相辅相成的。(我很高兴看到C#在改变,只是想知道是先有鸡还是先有蛋。)
我们进行特性设计并起草规范;足以编写原型并开始编写测试计划。原型实现和测试显示了规范不足的地方,因此我们相应地完善规范和实现。这不是鸡和蛋的问题,而是建筑师、建筑工人和居民共同努力建造建筑物的过程。
只是出于好奇,"扩展一切"不是很容易实现吗?我不确定,但它们不是仅仅看起来像某种类型的实际成员,但实际上并不是吗?我只是在思考它的需求和价值与实现成本的比较。如果你们决定不使用它,我想它可能不是那么容易。
"容易"意味着"我知道怎么做"。从这个意义上说,当然,它很容易;我们知道我们可以做到。但是,容易的特性并不一定廉价。我知道如何在不到一秒钟的时间内从整个互联网中搜索关键词;从算法的角度来看,搜索问题是很简单的,但是实现解决方案的工程问题是非常昂贵的。没有廉价的编译器特性。任何特性的设计、实现、测试和维护成本都是相当可观的。我们必须确保收益与成本相称。
下面是一个关于"trivial"含义的有趣讨论:“trivial”的意思是“不可能”。由于没有工程师会承认某个东西是不可能的,他们使用这个词来代替。当工程师说某个东西“不是微不足道的”时,这等于飞行员在将你带入五级飓风时冷静地告诉你可能会遇到“一点点颠簸”。
感谢您发布这个链接。看起来非常有用。之前我以为“微不足道”意味着对每个人来说都很容易,也就是每个人都可以轻松做到,但现在我明白了。不过,我想并不是每个程序员都按照您链接中描述的方式使用它,对吗?