扩展方法及其在软件工程中的意义(或者它真的是一个好主意吗?)

Ext*_*kun 8 extension-methods c#-3.0

是的,所以MS引入了C#3.0的扩展方法.基本上,它允许您向现有类添加新方法,而无需从中继承或更改其实现.我的脑海里立刻响起了警钟.

1)您开始为库或标准库类实现自己的扩展方法.现在它不再标准化了.

2)在OOP中,您可以通过继承或组合定义新类型.实际上扩展方法是什么呢?同样,在使用扩展方法时,没有定义明确的关系.

3)扩展方法是否可以在项目之间移植?如果存在两种方法的扩展方法怎么办?

扩展方法也是一大堆蠕虫值得避免的吗?我可以看到它有优点,因为你输入较少,但不知何故它看起来会导致不好的练习.

意见?

Meh*_*ari 9

扩展方法不会添加到类型中.它们只是语法糖.你不能访问私人的东西.扩展方法就像任何使用类型的静态方法一样.

他们可以做的最糟糕的事情是损害可读性并误导人们.除了语法之外,它们是不存在的.

更新(评论):

这正是我从"误导人"和"伤害可读性"的意思.幸运的是,Visual Studio在区分扩展方法(使用IntelliSense列表中的图标和工具提示)方面非常有用.就个人而言,我有点偏向于在框架的任意类中添加一堆扩展方法(除了sealed扩展方法很有意义的类),除非它们为特定项目提供了显着的好处.我更喜欢保持扩展方法主要用于继承层次结构的顶级接口和类.这将使开发人员只使用它们适用于框架中的许多情况,而不是将任何实用程序方法作为扩展(IMO,这是在整个命名空间中自动发现扩展方法的直接工件.我真的希望C#需要你添加using关于特定的指令class而不是自动导入a namespace)中的所有扩展.

根据我的经验,如果你没有过度使用扩展方法,通过使用几次(我见过一些人这样做)扩展的每个实用方法,它很少是一个维护问题.如果明智地使用它将提高可读性.

使用扩展方法来破坏类并不是一件好事.在极端情况下,它可能是危险的.有人可能会通过向类添加一个方法来引入名称冲突问题,该方法具有与现有扩展名相同的名称.这可能会破坏以前称为扩展名的代码片段,现在正在调用新方法.
在我看来,这是与扩展方法相关的最大问题.这使得扩展的良好命名非常重要.


Jon*_*eet 5

不,这不是一个值得避免的蠕虫.它可以简化生活,IMO允许您将类型的核心操作与"辅助"方法分开,这些方法可以仅从这些核心操作中实现.我一直试图以这种方式使用扩展方法,即使它们不是真的有必要(即我有一个基类来放入它们,我控制代码).

您需要了解,当导入包含扩展方法的相应命名空间时,扩展方法只是使静态方法调用看起来像实例方法的一种方法.特别是:

  • 不会向类型添加方法,因此它不会使标准库"非标准".它只是意味着如果您有可用的扩展方法,您可以更轻松地使用这些库.
  • 与扩展方法中定义的明确的关系:扩展方法声明其类型是"扩展"与第一个参数的类型.它不是组合或聚合,但我不明白为什么这是一个问题.如果某些东西是有用的并且实际上并没有使设计变得更糟,那么它是否是传统OO的一部分是否重要?
  • 是的,扩展方法可以在项目之间移植.与往常一样,如果同时可以看到两个具有相同名称的扩展方法,则会有规则.

接口上的扩展方法非常好,允许简单的方法链接等.我宁愿看到扩展方法调用而不是Java中的显式静态调用:

// Explicit helper method call
Collections.sort(collection);
// With extensions, this could be:
collection.sort();
Run Code Online (Sandbox Code Playgroud)

但有一些缺点:

  • 它不允许派生类型以更有效的方式覆盖实现.当然,派生类型可以声明具有相同签名的方法,但只有在编译时类型合适时才会使用.
  • 在C#中发现扩展方法的方式很痛苦.我在别处咆哮过这个......
  • 使用动态类型时,扩展方法不可用.

总的来说,我认为它们很可爱 - 当适度使用时,它们可以发挥重要作用.我当然不想在没有LINQ的情况下使用LINQ.


And*_*ite 5

扩展方法根本不修改现有的类,它们只是"语法糖"或用于将静态方法附加到类的编译器技巧.您可以通过在另一个类中创建一个普通的静态实用程序方法来实现相同的目的,该类将所需的类作为该方法的第一个参数.我看到了关于向类添加"方法"并因此对其进行去标准化的观点,但这并不是真正发生的事情,我实际上发现扩展方法直观且方便地向现有类添加特定于域或实用程序的方法否则将无法修改.


Dan*_*ker 5

你可以关闭闹铃.扩展方法只对语法产生非常小的差异,而没有其他方法.而不是写:

SomeClass.SomeStaticMethod(someVar, someArg);
Run Code Online (Sandbox Code Playgroud)

你写:

someVar.SomeStaticMethod(someArg);
Run Code Online (Sandbox Code Playgroud)

它只是交换排序并消除了类名限定符.否则,它是相同的.这样做的价值在于您可以键入变量名称,IDE可以在智能感知功能中为您提供有用的方法,并且您可以从左到右阅读代码,因此"嵌套"函数调用变得更具可读性.

您的所有顾虑同样适用于静态方法 - 因此不值得担心.

更新

扩展方法"假装做魔术"吗?只有你认为交换一些语法是神奇的!

你更有可能习惯于以某种方式看到写的东西,所以看到它们写成"向后"是令你惊讶的.但从其他语言的角度来看,现在是正确的方法.

有些语言允许任何非实例方法可选地使用方法名称之前的第一个参数写入,如果省略了点,那么这是一种支持中缀运算符的简洁方式,a + b而不必做任何特殊操作.

实例方法是启用此语法的一种方法,但如果您乐意不使用它,那么为了真正一致,为什么不要求像这样调用实例方法?

trimmed = string.Trim(str);
Run Code Online (Sandbox Code Playgroud)

这类似于F#的做法.毕竟,实例方法string可以被视为具有类型的第一个参数string.您通常不会将其视为参数,但它的工作方式大致相同,并且统一语法也有优势.

实例方法可以访问其定义的类的私有成员,而扩展方法则不能.但是,派生类中的实例方法无法访问基类的私有数据.无论如何,呼叫者为什么要关心这个?

注意事项

我不想给人的印象是扩展方法的所有用途都有意义.例如,是否有任何充分的理由object在无约束类型上进行扩展方法或泛型扩展方法T?在几乎任何上下文中,智能感知都会提出这样的事情,并且会变成类似于语言扩展的东西,例如With.

然后是关于扩展方法是否应该允许其第一个参数的辩论null.就个人而言,只要方法名称明确这一点,我认为string.IsNullOrEmpty这很好,所以我认为作为一种扩展方法会很好.但是其他人对此更加激进,我不在乎,所以我会采取"在罗马时"的态度.这是另一个例子:

public static void DisposeIfNotNull(this IDisposable d)
{
    if (d != null)
        d.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

该名称清楚地表明它包含了一个空检查(这就是它的全部要点).

  • 奇怪的是后者被认为是前者的语法糖.前者对我来说看起来更甜美,因为它很清楚,它说明发生了什么,它并没有假装做任何魔术. (3认同)