【问题标题】:Extension methods versus inheritance扩展方法与继承
【发布时间】:2010-11-11 01:29:04
【问题描述】:

是否有经验法则可以帮助确定在什么情况下使用哪个?大多数时候我应该更喜欢一个吗?

谢谢!

【问题讨论】:

  • 我个人,至少最近,喜欢扩展方法。但每当我学习新东西时,这对我来说都是正确的——一旦我学会了它,我就会不断地使用它:x :)

标签: c# .net class inheritance extension-methods


【解决方案1】:

扩展方法很有用,但它们比常规方法更难通过 IDE 发现,因为它们不附加到原始类,并且不知道它们的代码可能驻留在哪里。有一些 best practice suggestions 关于将它们放在哪里以及如何命名它们,但这些只是指导方针,并不能保证有人会遵循它们。

通常,如果您只是向一个众所周知的、常用的类或接口(例如 .Net 基类)添加功能,而您无权访问其代码,则通常会使用扩展方法。扩展方法也有一个限制,即您不仅必须拥有原始程序集,还必须拥有包含扩展方法的程序集,代码的使用者必须理解这一点。

使用继承将允许您添加、删除或覆盖功能,并确保在构建时它始终与类一起存在。

【讨论】:

    【解决方案2】:

    当您想要提供跨多种类型的实现时,应该使用扩展方法,这些类型应该共享相同的行为,但否则会有所不同。这就是为什么您会经常看到在接口上使用扩展方法的原因,因为它是一个非常强大的工具,可以确保接口的任何给定实现都具有给定行为的相同实现。

    例如,Skip 和 Take 扩展方法。

    【讨论】:

    • 或类似 LINQ 的东西,它适用于可枚举(或集合?)类型?
    • @Steve 没错,也为 LINQ 实现了 Skip 和 Take。
    • Skip 和 Take 扩展方法在 System.Linq.Queryable 类中,操作 IQueryable 接口,返回一个 IQueryable,这使得它们也可以链接,即 Skip().Take()
    • 所以我想如果我是 LINQ 的设计者,我会更喜欢扩展方法,因为无论如何 LINQ 调用总是暗示一个可枚举作为第一个参数,MyList.DoStuff(parameters...) 会优于 LINQ.DoStuff(MyList, parameters...)
    • 好点。扩展方法是为 Linq 发明的,所以我猜它们解决了很多问题。
    【解决方案3】:

    嗯...你不能总是使用继承。例如,String 是一个密封类。正是在这种情况下,扩展方法才真正发挥作用。

    一般来说,扩展方法最适合小实用程序,否则您可能会将这些实用程序放入静态类中,但要针对特定​​类型的实例进行操作。字符串就是一个很好的例子——几乎每个人都有自己的小字符串扩展方法来对字符串进行少量操作。

    扩展方法的另一个好地方是反对枚举。我几乎总是针对我创建的任何[Flags] 枚举包含一个HasFlag 扩展方法。

    【讨论】:

    • 好的,那这和作文有什么关系呢?
    • 这与作文无关。请参阅我对您的回复的评论。
    【解决方案4】:

    尽可能使用继承而不是扩展方法

    编辑

    我更喜欢保持简短,但我当然会回答后续问题。

    在可以继承的情况下,也就是说没有密封的类,它几乎总是比扩展方法更好的选择。事实上,这就是 womp 引用的 best practices document 所说的。它有诸如“小心扩展方法”、“在扩展不属于你的类型之前三思”和“更喜欢接口扩展而不是类扩展”等标题。换句话说,它只是更详细地说明了我的单线器所做的事情。

    这篇文章确实给出了详细的原因,但最重要的是,这就是扩展方法的设计方式。它们在游戏后期被添加到语言中,作为语法糖,让 MS 可以插入 LINQ,而不必回去重新发明轮子。这是他们擅长的典型例子。另一个很好的例子是添加实用方法,例如:

    public static string FormatWith(this string format, params object[] args)
    { return string.Format(CultureInfo.InvariantCulture, format, args); }
    

    请注意,在这种情况下,扩展方法是完成此附加功能的唯一方法,因为字符串是密封的。

    至于组合优于继承,虽然这是不言而喻,但我看不到这里的相关性。无论我们使用扩展方法还是继承,目标都是改变接口以允许其他方法。这种方法的实现方式,无论是通过组合、泛型还是其他技术,都是正交的。

    【讨论】:

    • @Steven 我很想知道你为什么这么认为?
    • 优先组合而不是继承。 -- artima.com/lejava/articles/designprinciples4.html
    • 使用密封类,除了使用扩展方法,您别无选择......
    • 是的,密封类是不可能继承的一个很好的例子,所以扩展方法值得考虑。
    • 当你说“尽可能使用继承”时,人们会对此做出反应,仅此而已。
    【解决方案5】:

    它们非常不同,例如 LINQ 标准查询运算符是扩展方法的一个很好的例子,应该很难通过继承来实现,但是如果您可以访问类并且可以更改源,那么使用继承会更好,
    编辑
    这是我在这里找到的一些规则C# 3.0 Features: Extension Methods

    • 扩展方法不能用于覆盖现有方法
    • 与实例方法具有相同名称和签名的扩展方法将不会被调用
    • 扩展方法的概念不能应用于字段、属性或事件
    • 谨慎使用扩展方法....过度使用可能是一件坏事!

    【讨论】:

      【解决方案6】:

      我会坚持继承,除非扩展方法主要设计用于扩展密封类或创建特定于范围的扩展。还要记住它们是静态的,所以如果这些是您需要在其他类中覆盖的方法和行为,那么您就不能真正使用扩展。

      扩展方法确实有一个非常棒的特性,这是它们实现的固有优势。由于它们是静态方法,因此您可以在空对象上调用它们。

      例如:

      string a = null;
      return a.IfNullOrEmpty("Default Value");
      

      这样的实现很棒,尽管它们在技术上只是语法糖。恕我直言,任何能让你的代码更干净、更易读的东西都很棒。

      虽然我不喜欢他们不是真的被发现。如果我将该代码从一个类复制到另一个类,我将不得不搜索定义它的命名空间。

      【讨论】:

      • 这是一个很好的例子。其实我写的第一个扩展方法是: public static bool IsNullOrEmpty(this string s) { return string.IsNullOrEmpty(s); }
      • 我编写的大多数扩展都是为了利用空对象\列表而设计的。我喜欢这个功能。
      • 这是不能选择继承的一个很好的例子。
      • 还有一个,涉及 WinForm 代表:stackoverflow.com/questions/714666/…
      【解决方案7】:

      这真的取决于您需要解决的问题,在大多数情况下,类继承和接口自然比扩展方法更有意义,因此应该首选。

      另一方面,Extensions 允许您创建有用的方法来应用 而不仅仅是一个 类 - 否则使用 inheritance 会更加麻烦,如果不是几乎不可能实现的话。

      最后但同样重要的是,Extensions 允许您扩展 .NET Framework 的内置类 以及 3rd party classes,,即使您不这样做'不拥有或无权访问源代码。

      以下是一些使用扩展方法的例子:


      LinqPad 使用扩展方法,例如.Dump() 方法,您可以使用该方法将各种对象的内容转储(打印)到输出窗口。


      .NET 框架 本身在很多地方都使用了扩展方法,例如在Linq:

      public static TSource FirstOrDefault<TSource>(this 
                              System.Collections.Generic.IEnumerable<TSource> source)
      

      返回any可枚举集合任意对象类型的第一个元素或默认值。


      扩展方法优于继承的示例如下: 假设您想创建一个能够创建任何现有对象的clone(副本) 的方法。使用扩展(和泛型,加上反射)你可以做到this way

      【讨论】:

        【解决方案8】:

        扩展方法破坏了良好的 OO 设计。说它们应该用于您无权访问代码库的密封类是荒谬的。密封且您无权访问的类可能出于某种原因(性能、线程安全)而被密封,并且盲目地为这些类标记功能是非常危险的。总有一种方法可以以纯 OO 的方式实现装饰器模式,如果不这样做,会使代码更难阅读、维护和重构。根据经验,如果一种语言的特性闻起来很糟糕,那么应该避免使用它。我相信您可以找到一个示例,其中扩展方法很有用,但事实是,这些功能将被那些接受过最少 OO 培训的开发人员滥用。

        【讨论】:

        • @AustinHenley 可能是最少的面向对象培训? :)
        【解决方案9】:

        MSDN

        C# programming guide 的扩展方法页面上写着:

        一般准则

        一般而言,我们建议您谨慎实施扩展方法,并且仅在必要时才实施。只要有可能,必须扩展现有类型的客户端代码应该通过创建从现有类型派生的新类型来实现。有关详细信息,请参阅继承(C# 编程指南)。

        当使用扩展方法来扩展您无法更改其源代码的类型时,您将面临更改类型实现将导致扩展方法中断的风险。

        【讨论】:

          猜你喜欢
          • 2014-02-15
          • 2017-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-03
          相关资源
          最近更新 更多