9

我最近一直在玩弄使用扩展方法在我控制的类上实现帮助实用程序的想法(即,在同一个程序中并且我可以修改)。其背后的基本原理是,很多时候,这些辅助实用程序用于非常特定的场景,并且不需要访问类的内部值。

例如,假设我有一个 StackExchange 类。它会有类似PostQuestionandSearch和的方法AnswerQuestion

现在,如果我想手动计算我的声誉以确保 StackOverflow 没有欺骗我怎么办。我会按照以下方式实施一些事情:

int rep=0;
foreach(var post in StackExchangeInstance.MyPosts)
{
  rep+=post.RepEarned;
}

我可以向 StackExchange 类添加一个方法,但它不需要任何内部结构,而且它仅用于程序的一两个其他部分。

现在想象一下,如果您有 10 或 20 个这些特定的辅助方法。在某些情况下肯定有用,但绝对不适用于一般情况。我的想法正在改变类似的东西

public static RepCalcHelpers
{
  public static int CalcRep(StackExchange inst){ ... }
}

像这样的东西

namespace Mynamespace.Extensions.RepCalculations
{
  public static RepCalcExtensions
  {
    public static int CalcRep(this Stackexchange inst){...}
  }
}

注意命名空间。理想情况下,我会使用它来对特定场景中的扩展方法进行分组。例如,“RepCalculations”、“Statistics”等。

我已经尝试搜索是否听说过这种类型的模式,并且没有发现任何扩展方法被用于任何东西的证据,除了你不能修改的类。

这种“模式”有哪些不足之处?我应该坚持继承或组合,还是只是一个很好的静态帮助类?

4

4 回答 4

4

我会阅读关于扩展方法的框架设计指南部分。是第二版的一位作者的帖子。您描述的用例(专门的辅助方法)被 Phil Haack 引用为扩展方法的有效用途,其缺点是需要额外的 API 知识才能找到那些“隐藏”方法。

在那篇文章中没有提到但在书中推荐的是扩展方法进入与扩展类不同的命名空间。否则,它们将始终以智能感知出现,并且无法关闭它们。

于 2012-12-28T17:05:22.093 回答
0

我想我在其他地方看到过这种模式。它可能很混乱,但也很强大。这样,您可以在库中提供一个类,并在单独的命名空间中提供一组扩展方法。然后,使用您的库的任何人都可以选择使用您的扩展方法导入命名空间或提供他们自己的扩展方法。

如果您有一些仅用于单元测试的扩展方法(例如,比较两个对象是否相等,那么您只需要进行单元测试)是这种模式的一个很好的候选者。

于 2012-12-28T17:14:58.937 回答
0

您似乎在比较扩展方法等效于公共实例方法。真的不是。

扩展方法只是一个公共静态实用程序方法,恰好具有更方便的调用语法。

所以首先我们要问自己,这个方法是类本身的实例方法合适,还是外部类的静态方法更合适。很少有类的用户需要这个功能,因为它是高度本地化的,而不是类本身执行的真正行为,而是外部实体在类上执行的行为,这意味着它适合静态。User主要缺点是,如果有人有 a并且想要重新计算他​​们的代表,那么这种行为可能更难找到。现在,在这种特殊情况下,它有点在围栏上,你可以走另一条路,但我倾向于静态方法。

现在我们已经决定它应该是静态的,它是否应该是一个扩展方法是一个完全独立的问题。这更加主观,并进入个人偏好领域。这些方法可能会被链接起来吗?如果是这样,扩展方法的链接比对静态方法的嵌套调用要好得多。它是否可能在确实使用它的文件中被大量使用?如果是,扩展方法可能会稍微简化代码,如果不是,它并没有那么大的帮助,甚至是有害的。对于玩具示例,我可能会说我个人不会,但我不会对做过的人有任何问题(毕竟你仍然可以使用扩展方法,就好像它是一个常规的公共静态方法语法明智)。对于非玩具示例,这主要是逐案决定。

还值得一提的是,在一些极端情况下,扩展方法可能比实例方法更强大。特别是通过利用类型推断。使用常规实例方法很容易接受类型或该类型的任何子类型,但有时返回传入的任何类型而不是父类型很有用。这特别用于流畅的 API。不过,这不是一个非常常见的示例,并且仅与您的问题松散相关,因此我不会对此进行扩展。

于 2012-12-28T17:15:10.913 回答
0

在您的类实现接口并且您希望避免在实现相同接口的其他“未来”类上实现相同方法的情况下,扩展方法可能非常有用。例如,StackExchange 实现了 IStackExchange,ProgrammersExchange 也实现了 IStackExchange。您的示例扩展方法对于只实现一次 CalcRep 很有用,而不必在两个类上重新实现它。这正是静态 Enumerable 类中存在所有扩展方法的原因。

除此之外,我没有看到在您已经可以修改的类上使用扩展方法的令人信服的理由。如果有的话,它的缺点是在重载解决过程中被认为是后期。

于 2012-12-28T17:20:46.793 回答