“谨慎使用推广方法”背后的动机是什么?

我发现它们是扩展现有类的一种非常自然的方式,特别是当您只需要将某些function“点焊”到现有类时。

微软表示,“一般来说,我们建议您谨慎实施扩展方法,并且只在必要时才实施。” 然而,扩展方法构成了Linq的基础; 事实上,Linq是创建扩展方法的原因。

是否存在使用扩展方法而不是inheritance或组合的特定设计标准? 他们气馁的标准是什么?

我们向C#MVP展示了提出的新语言function(至少是那些有一半看到光明的机会)以获得早期反馈。 我们经常在许多function上得到的一些反馈是“ 会明智地使用这个function并按照function的设计原则,但是我的goofball同事会对这个东西疯狂,写出一堆不可维护的东西,不可理解的代码,我将在接下来的十年中遇到调试问题,所以我希望它能够实现这个function!“

虽然我在某种程度上夸大了喜剧效果,但这是我们非常认真对待的事情。 我们希望设计新function以鼓励他们使用,但不鼓励他们滥用。

我们担心扩展方法会被用来,例如,随意地不加区分地扩展“对象”,从而创建一个松散类型的泥球,难以理解,调试和维护。

我个人特别关注你在“流利”编程中看到的“可爱”扩展方法。 好像的东西

6.Times(x=>Print("hello")); 

呸。 “for”循环在C#中普遍可以理解和惯用; 不要变得可爱

问题是扩展方法不一定清楚。

当你在代码中看到类似的东西时:

  myObject.Foo(); 

自然本能是Foo()是在myObject的类上定义的方法,或者是myObject类的父类。

使用Extension方法,这是不正确的。 几乎任何类型都可以定义几乎任何类型的方法(你可以在System.Object上定义一个扩展方法,虽然这是一个坏主意……)。 这确实增加了代码的维护成本,因为它降低了可发现性。

每当您添加不明显的代码,甚至降低实现的“显而易见性”时,就长期可维护性而言都会涉及成本。

过度使用扩展方法是不好的,原因与过度使用重载运算符的原因相同。 让我解释。

让我们先看看运算符重载示例。 当你看到代码时:

 var result = myFoo + myBar; 

你希望myFoomyBar是数字类型,而operator +正在执行加法。 这是合乎逻辑的,直观的,易于理解的。 但是一旦运算符重载进入图片,你就无法确定myFoo + myBar实际上在做什么 – +运算符可能会被重载以表示任何意思 。 您不仅可以阅读代码并找出正在发生的事情,而无需阅读表达式中涉及的所有类型的代码。 现在, operator +已经为StringDateTime等类型重载了 – 但是,对于这些情况下的添加意味着直观的解释。 更重要的是,在这些常见情况下,它为代码增加了许多表达能力。 因此值得潜在的混淆。

那么所有这些与扩展方法有什么关系呢? 好吧,扩展方法引入了类似的情况。 没有扩展方法,当你看到:

 var result = myFoo.DoSomething(); 

你可以假设DoSomething()myFoo的方法或其中一个基类。 这很简单,易于理解,甚至直观。 但是使用扩展方法, 可以在任何地方定义 DoSomething() – 更糟糕的是,定义依赖于代码文件中的using语句集合,并且可能会将许多类引入混合中,其中任何一个都可以承载DoSomething()的实现DoSomething()

现在不要误会我的意思。 运算符重载和扩展方法都是有用且强大的语言function。 但请记住 – 强大的力量带来了巨大的责任 。 您应该在提高实现的清晰度或function时使用这些function。 如果你不加选择地开始使用它们,它们会给你想要创建的东西增加混乱,复杂性和可能的​​缺陷。

我遵循一个非常简单的规则,包括扩展方法和辅助类。

任何时候我都有一个可以降级到静态助手类的方法,我会在一般范围内权衡它的用处,我真的希望这个方法能够被共享吗? 意义是否明确且对他人有用?

如果我能对这个问题回答“是”,我会将其创建为扩展方法。 我有一个共享库,其中包含1个命名空间中的所有Extension方法,每个类文件定义为TypeNameExtension,因此很清楚在该类中可以期待什么,以便您可以轻松找到代码。

如果我质疑是否需要将辅助方法作为全局可访问的用法,我将声明方法private static并将其保留在拥有类中。

当Borland发明它们(Delphi Class Helpers)时,指导原则已经是:“应该仅在特殊情况下使用”。

它们已经变得更加被接受(作为LINQ的一部分),但它们仍然是添加function的奇怪方式。 我认为里德对原因有很好的答案。

我有一个不断增长的扩展方法库,可以在BCL类上运行,以简化常见问题,乍一看看起来很混乱。 坦率地说,我认为扩展方法使事情更具可读性。 例如,进行if语句测试以查看数字是否在两个数字之间:

 if (x <= right && x >= left ) { // Do Something } 

如果right事情少于事故还left怎么办? 这究竟是做什么的? 当变量简单地像xleft ,那么它很容易进行。 但如果他们是长class的财产怎么办? if语句可以变得非常大,这会混淆你想要做的相当简单的事情。

所以我写了这个:

 public static bool Between(this T test, T minValue, T maxValue) where T : IComparable { // If minValue is greater than maxValue, simply reverse the comparision. if (minValue.CompareTo(maxValue) == 1) return (test.CompareTo(maxValue) >= 0 && test.CompareTo(minValue) <= 0); else return (test.CompareTo(minValue) >= 0 && test.CompareTo(maxValue) <= 0); } 

现在我可以改进我的if语句:

 if (x.Between(left, right)) { // Do Something } 

这是“非凡的”吗? 我不这么认为......但我认为这是对可读性的重大改进,并且它允许对测试输入进行一些额外的检查以确保安全。

我认为微软希望避免的危险是人们编写了大量的扩展方法来重新定义EqualsToString

扩展方法可以被认为是面向方面的。 我相信微软说,如果你有源代码,你应该改变原来的类。 它与清晰度和易维护性有关。 需要扩展方法的原因是它们扩展了现有对象的function。 在LINQ的情况下,如果你深入研究它的工作原理,可以使用各种类型的对象创建,这些类型无法提前知道。 使用扩展方法可以添加一些行为。

例如,扩展.NET框架对象是有意义的,因为您没有源,但可能有一些行为要添加到对象。 扩展字符串,DateTime和FrameworkElement类是可以接受的。 为跨越应用程序边界的类创建扩展方法也是可以接受的。 比如,如果您想共享DTO类并具有某些特定的UI行为,请仅在UI中创建该扩展方法。

使用动态语言,范例是不同的,但我相信你在谈论C#和VB.net。