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

时间:2010-03-26 15:52:00

标签: c# extension-methods

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

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

是否存在使用扩展方法而不是继承或组合的特定设计标准?他们劝阻什么标准?

7 个答案:

答案 0 :(得分:17)

我们向C#MVP展示了提议的新语言功能(至少是那些有一半看到光明的机会)以获得早期反馈。我们经常在许多功能上获得的一些反馈是“我会明智地使用此功能,并根据功能的设计原则,但我的goofball同事将会用这个东西搞疯了,写下一大堆不可维护的,不可理解的代码,我将在接下来的十年中遇到调试问题,所以尽我所能,请不要实现这个功能!“< / p>

虽然我对喜剧效果有点夸张,但这是我们非常认真对待的事情;我们希望设计新功能以鼓励他们使用,但不鼓励他们滥用。

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

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

这样的东西
6.Times(x=>Print("hello"));

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

答案 1 :(得分:11)

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

当您在代码中看到类似内容时:

 myObject.Foo();

天生的本能是Foo()是在myObject的类或myObject类的父类上定义的方法。

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

任何时候添加非显而易见的代码,甚至降低实施的“显而易见性”,都会产生长期可维护性的成本。

答案 2 :(得分:6)

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

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

var result = myFoo + myBar;

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

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

var result = myFoo.DoSomething();

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

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

答案 3 :(得分:3)

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

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

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

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

答案 4 :(得分:2)

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

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

答案 5 :(得分:2)

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

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

如果right在事故中小于left怎么办?这究竟是做什么的?当变量简单地类似于xleft时,它很容易理解。但如果他们是长班的财产怎么办? if语句可以变得非常大,这会使你想要做的事情变得模糊不清。

所以我写了这个:

public static bool Between<T>(this T test, T minValue, T maxValue)
    where T : IComparable<T>
{
    // 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

答案 6 :(得分:1)

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

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

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