扩展方法与inheritance
有什么经验法则可以帮助确定在哪种情况下使用哪种? 我应该比其他大多数时间更喜欢一个吗?
谢谢!
扩展方法是有用的,但是通过IDE比常规方法更难以发现,因为它们没有附加到原始类,也没有关于它们的代码可能位于何处的线索。 关于在哪里放置它们以及如何命名它们,有一些最佳实践build议 ,但这些只是指导方针,并不能保证有人会遵循它们。
通常情况下,如果您只是将function添加到众所周知的良好使用的类或接口(如.Net基类)中,则无法访问代码,因此通常会使用扩展方法。 扩展方法还有一个约束,你不仅要拥有原始的程序集,而且必须拥有扩展方法的程序集,这个方法必须被你的代码消费者所理解。
使用inheritance将允许您添加,删除或覆盖function,并确保它在构build时始终与类一起出现。
当你想要提供跨多种types的实现时,应该使用扩展方法,这些types应该共享相同的行为,否则会是不相似的。 这就是为什么你会看到接口上使用扩展方法的原因,因为它是一个非常强大的工具,可以确保接口的任何给定实现将具有相同的给定行为的实现。
例如,跳过和延伸方法。
那么…你不能总是使用inheritance。 例如, String
是一个密封的类。 在这种情况下,扩展方法真的很闪耀。
一般来说,扩展方法对于那些可能放入静态类的小实用程序来说是最好的,但是对于特定types的实例来说,它们是可以操作的。 string是一个很好的例子 – 几乎每个人都有自己的小string扩展方法来对string进行小操作。
扩展方法的另一个好地方是反对枚举。 我几乎总是包含一个HasFlag
扩展方法,以防止我创build的任何[Flags]
枚举。
只要有可能,就使用inheritance而不是扩展方法 。
编辑
我宁愿保持简短,但我当然会回答后续问题。
在可能inheritance的情况下,也就是说没有密封的类,几乎总是比扩展方法更好的select。 实际上,这是womp引用的最佳实践文档所说的。 它具有诸如“谨慎扩展方法”,“在扩展你不拥有的types之前考虑两次”和“优先考虑类扩展的接口扩展”的标题。 换句话说,它只是说了我的一行所做的,更详细的。
文章确实给出了详细的原因,但底线是这是如何devise使用扩展方法。 他们在游戏后期join了一些语法糖,让MS在LINQ中进行楔入,而不必回头重新发明轮子。 这是他们所擅长的典型例子。 另一个很好的例子是添加实用方法,例如:
public static string FormatWith(this string format, params object[] args) { return string.Format(CultureInfo.InvariantCulture, format, args); }
请注意,在这种情况下,扩展方法是完成这一附加function的唯一方法,因为string是密封的。
至于构成而不是inheritance,虽然这是一个真理,但我在这里看不到相关性。 无论我们使用扩展方法还是inheritance,我们的目标都是改变接口以允许使用其他方法。 这种方法是如何实现的,无论是通过组合,generics还是其他技术,都是正交的。
他们是非常不同的,例如LINQ标准查询运算符是扩展方法的很好的例子,应该很难用inheritance来实现,但是如果你有权访问类并且可以改变源代码,那么使用inheritance会更好,
编辑
这里有一些规则,我在这里findC#3.0function:扩展方法
- 扩展方法不能用来覆盖现有的方法
- 具有与实例方法相同名称和签名的扩展方法将不会被调用
- 扩展方法的概念不能应用于字段,属性或事件
- 谨慎使用扩展方法….滥用可能是一件坏事!
我会坚持inheritance,除非扩展方法主要是为扩展密封类或创build特定于范围的扩展而devise的。 另外请记住,它们是静态的,所以如果这些是您需要在其他类中重写的方法和行为,则不能真正使用扩展。
扩展方法确实有一个非常重要的特性,这是它们实现的固有优势。 由于它们是静态方法,所以可以在空对象上调用它们。
例如:
string a = null; return a.IfNullOrEmpty("Default Value");
像这样的实现是很好的,虽然他们在技术上只是语法上的糖。 恕我直言,任何让你的代码更干净和更具可读性的东西都很棒。
虽然我不喜欢,但是他们不是真的可以发现的。 如果我将这些代码从一个类复制到另一个类,那么我将不得不search它所在的名称空间。
这实际上取决于你需要解决的问题,在大多数情况下, 类inheritance和接口自然比扩展方法更有意义,因此应该是首选。
另一方面, 扩展允许您创build有用的方法,不仅适用于一个类,如果不是几乎不可能实现的话, inheritance将会更麻烦。
下面是一些使用扩展方法的例子:
LinqPad使用扩展方法,例如.Dump()
方法,可以将每种对象的内容转储(打印)到输出窗口。
.NET框架本身在许多地方使用扩展方法,例如在Linq中:
public static TSource FirstOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source)
它返回任何对象types的任何可枚举集合的第一个元素或默认值。
例如,扩展方法比inheritance更好的例子如下:假设你想创build一个能够创build任何现有对象的克隆 (复制)的方法。 有了扩展(和generics,加上reflection),你可以这样做。
扩展方法打破了良好的OOdevise。 说他们应该使用密封类,你没有访问代码库是荒谬的。 被密封但你无法访问的类可能因为某种原因(性能,线程安全性)被密封,并且盲目地为这些类标记function是非常危险的。 总是有一种以纯粹的面向对象的方式实现装饰器模式的方法,而不是这样做使得代码难以阅读,维护和重构。 作为一个经验法则,如果一个语言的特征闻起来不好,那就应该避免。 我相信你可以find一个例子,其中扩展方法是有用的,但事实是,这些function将被最小的面向对象培训的开发人员滥用。