在C# 3.0中评估使用扩展方法的成本与收益。

8
在什么情况下(使用场景)你会选择编写扩展而不是子类化对象?完全透明:我不是微软员工;我不认识三津浮田;我确实认识提到的开源Componax库的作者,但我与他没有任何商业往来;我不创建也不计划创建任何使用扩展的商业产品:总之:这篇文章纯粹出于我的智力好奇心,与我不断了解“最佳实践”有关。我发现扩展方法的想法“很酷”,显然你可以像Mitsu Furota(MS)博客文章link text中的许多示例一样做“远离”事情。一个个人朋友编写了开源Componax库link text,里面有一些非凡的设施;但他完全掌握了他的小公司,对代码指南拥有完全控制,并且每行代码“都经过他的手”。
虽然这只是我个人的猜测,但我认为在中大型软件团队使用扩展时可能会涉及其他问题。
查看微软的指南link text,您会发现:
一般来说,您可能会更频繁地调用扩展方法而不是实现自己的方法。我们建议仅在必要时适度使用扩展方法。每当可能时,必须扩展现有类型的客户端代码应通过创建从现有类型派生的新类型来完成。有关更多信息,请参见继承(C#编程指南)...当编译器遇到方法调用时,它首先查找类型的实例方法。如果没有找到匹配项,则会搜索为该类型定义的任何扩展方法,并绑定到它找到的第一个扩展方法。
还有微软的link text

扩展方法没有特定的安全漏洞。它们永远不能用于冒充类型上现有的方法,因为所有名称冲突都会被解析为由类型本身定义的实例或静态方法。扩展方法无法访问扩展类中的任何私有数据。

我认为显而易见的因素包括:

  1. 我假设您不会编写扩展方法,除非您期望它被广泛且频繁地使用。另一方面:您不能说子类化也是同样的吗?

  2. 知道我们可以将它们编译成单独的dll,并添加编译后的dll并引用它,然后使用扩展:“很酷”,但是否“平衡了”编译器首先必须检查是否定义了实例方法,如上所述。或者在“名称冲突”的情况下,使用Static调用方法确保调用扩展而不是实例定义的成本?

频繁使用扩展方法如何影响运行时性能或内存使用:我不知道。

因此,我希望您能分享您的想法,或者了解何时使用扩展和何时不使用,与子类化相比。

谢谢,比尔


1
有很多其他问题涵盖了同样的主题: https://dev59.com/sHRB5IYBdhLWcg3w3K0J - snicker
1
使用扩展方法的频率会如何影响运行时性能或内存使用:我不知道。实际上,它对二者都没有任何影响。静态方法调用与调用具有相同参数数量(在后一种情况下计算隐式this)的非虚拟、非接口实例方法完全相同。 - Pavel Minaev
4个回答

12

我的主要用途是扩展封闭的第三方API。

如今,大多数软件开发人员在Windows上提供API时,越来越倾向于使用.NET进行扩展性开发。我喜欢这样做,因为我更喜欢依赖自己的方法,未来可以修改,并作为他们API的全局入口点,在他们更改API时也不会受到影响。

以前,在必须这样做的情况下,如果不能继承API对象,因为它被封闭了或其他原因,我将依靠适配器模式来创建包装他们对象的自己的类。这是一个功能性但相当不优雅的解决方案。扩展方法为您提供了一种美妙的方式,可以向您无法控制的东西添加更多功能。

许多其他人最常使用它们的方式是使用LINQ

如果没有提供给IEnumerable的扩展方法,LINQ将无法实现。

人们喜欢它们的原因是因为它们使代码更易读

我注意到另一个重要的扩展方法的用途(包括我自己)是使代码更易读,并使其看起来好像做某事的代码属于它应该的位置。它还消除了许多人遇到过的可怕的“Util”静态类。哪一种更好看? Util.DecimalToFraction(decimal value); 还是 value.ToFraction();?如果您和我一样,会选择后者。

最后,有些人认为“静态方法”是邪恶的

许多“优秀的程序员”会告诉你应该尽量避免使用静态方法,特别是那些进行广泛单元测试的人。有时候静态方法很难测试,但如果使用得当,它们并不是邪恶的。而扩展方法确实是静态的……但它们看起来并不像是静态的。这样可以将那些静态方法从类中抽离出来,并附加到它们真正应该所属的对象上。

关于性能...

扩展方法与调用静态方法没有什么不同,只是将被扩展的对象作为参数传递...因为编译器会将其转换成这样的形式。好处在于你的代码看起来干净,它能做你想要的事情,编译器为你处理了繁琐的工作。


感谢Snicker提供的非常周到的回复和在评论中提供的SO上其他消息的链接。我确实阅读了许多其他帖子,但仍然觉得有必要提出自己的“风味”问题。我已经是使用静态方法、字段等的“粉丝”,对我来说,您提出的可读性问题是使用扩展方法的一个明确动机。如果我们最终能够使用扩展属性,我也准备好了 :)最好的问候,比尔 - BillW 0秒前 - BillW

4
我使用扩展方法来提高类的功能而不增加类的复杂性。您可以保持类的简单性,然后将重复的工作作为扩展添加到类中。
Min() 和 Max() 扩展方法是很好的例子。您可以声明一个私有方法来计算这些值,但扩展方法提供更好的可读性,使功能在整个项目中可用,并且不需要使数组成为任何更复杂的对象。

2
采用子类化方法和扩展方法需要满足以下几点要求:
  1. 类型必须是可扩展的(非密封)
  2. 创建该类型的所有位置必须支持某种工厂模式,否则其他代码将只会创建基本类型。
添加扩展方法实际上只需要使用C# 3.0+编译器即可。
但最重要的是,继承层次结构应该表示is-a关系。我认为向一个类添加1或2个新方法/行为并不能真正表达这种类型的关系。相反,包装类或扩展方法更适合此场景。

1
在某些情况下,您无法使用子类:例如字符串是密封的。但是,您仍然可以添加扩展方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接