在可以修改的类上使用扩展方法是否可以接受

时间:2012-12-28 16:56:57

标签: c# .net design-patterns extension-methods

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

例如,假设我有一个StackExchange类。它有PostQuestionSearch以及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 个答案:

答案 0 :(得分:4)

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

该帖子中没有提到,但书中推荐的是扩展方法从扩展类进入单独的命名空间。否则,它们将始终显示为intellisense,并且无法将其关闭。

答案 1 :(得分:0)

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

这种模式的一个很好的选择是,如果你有一些扩展方法只用于单元测试(例如,比较两个对象在某种意义上是否相等,你只需要进行单元测试)。

答案 2 :(得分:0)

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

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

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

现在我们已经确定它应该是静态的,这是一个完全独立的问题,它是否应该是一种扩展方法。这更加主观,并进入个人偏好领域。这些方法是否可能被链接?如果是这样,扩展方法链接比嵌套调用静态方法更好。是否可能在使用它的文件中大量使用?如果是,扩展方法可能会稍微简化代码,如果不是,它实际上没有那么多帮助,甚至是伤害。对于玩具示例,我可能会说我个人不会,但我不会对有人做过任何问题(毕竟你仍然可以使用扩展方法,就好像它是一个常规的公共静态方法语法明智)。对于一个非玩具的例子,它主要是个案决定。关键是要注意你扩展的类,并问自己一个用户是否愿意混淆类型的Intellisense只是为了更方便地调用方法(这再次回到每个文件使用它的程度)在)。

还值得一提的是,有一些边缘情况,扩展方法可以比实例化方法更强大 。特别是通过利用类型推断。使用常规实例方法很容易接受该类型的类型或任何子类型,但有时返回传入的类型而不是父类型是有用的。这尤其适用于流畅的API。这不是一个非常常见的例子,而且只与你的问题松散相关,所以我不会对此进行扩展。

答案 3 :(得分:0)

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

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