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

发布于 2024-07-30 03:47:05 字数 1432 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

倾听心声的旋律 2024-08-06 03:47:05

扩展方法不会添加到类型中。 它们只是语法糖。 您无法访问私人内容。 扩展方法就像使用类型的任何静态方法一样。

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

更新(重新评论):

这正是我所说的“误导人们”和“损害可读性”。 幸运的是,Visual Studio 在区分扩展方法方面非常有帮助(使用 IntelliSense 列表和工具提示中的图标)。 就我个人而言,我有点偏向于向框架的任意类添加一堆扩展方法(除了密封类,其中扩展方法可以使很多意义)除非它们为特定项目提供了显着的好处。 我更喜欢将主要用于接口和类的扩展方法保留在继承层次结构的顶层。 这将使开发人员仅在适用于框架中许多情况的情况下使用它们,而不是将任何实用方法作为扩展(IMO,这是整个命名空间中扩展方法自动发现的直接工件。我真的希望 C# 要求您添加特定上的using指令,而不是自动导入命名空间中的所有扩展)。

根据我的经验,如果您不过度使用扩展方法,将每个使用几次的实用方法(我见过有些人这样做)作为扩展,那么它很少会成为维护问题。 如果明智地使用它会提高可读性。

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

Extension methods are not added to a type. They are just syntactic sugar. You can't access private stuff. An extension method is just like any static method that consumes a type.

Worst thing they can do is to harm readability and mislead people. Beyond the syntax, they are nonexistent.

UPDATE (re comment):

This is exactly what I meant from "misleading people" and "harming readability." Fortunately, Visual Studio is pretty helpful in distinguishing extension methods (using an icon in the IntelliSense list and the tooltip). Personally, I'm a little biased against adding a bunch of extension methods to arbitrary classes of the framework (with the exception of sealed classes where extension methods can make a lot of sense) unless they provide a significant benefit for the particular project. I prefer keeping extension methods mostly for interfaces and classes on top levels of inheritance hierarchy. This will make developers only use them where they are applicable to many cases in the framework rather than making any utility method an extension (IMO, this is direct artifact of autodiscovery of extension methods in a whole namespace. I really wish C# required you to add a using directive on a the specific class rather than automatically importing all extensions in a namespace).

In my experience, if you don't overuse extension methods by making every utility method that's used a few times (I've seen some people do this) an extension, it's rarely a maintenance problem. It will improve readability if used wisely.

Cluttering up classes with extension methods is not a good thing. At the extreme cases, it can be dangerous. One might introduce a name collision issue by adding a method to class with the same name of an already existing extension. This can break code snippets that previously called the extension and are now calling the new method.
In my opinion, this is the biggest issue associated to extension methods. This makes good naming of extensions very important.

断舍离 2024-08-06 03:47:05

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

Extension methods do not modify the existing class at all, they are just "syntactic sugar" or a compiler trick for attaching a static method to a class. You can accomplish the same end by just creating a normal static utility method in another class that takes the desired class as the first argument to the method. I see your point about "adding" methods to a class and thereby de-standardizing it, but this is not really what's happening, and I actually find extension methods to be intuitive and convenient for adding domain-specific or utility methods to an existing class that would otherwise be unmodifiable.

谈下烟灰 2024-08-06 03:47:05

不,这不是一个值得避免的蠕虫。 它可以使生活变得更加简单,并且在我看来,它允许您将类型的核心操作与“帮助程序”方法分开,这些方法可以仅从这些核心操作中实现。 我一直想以这种方式使用扩展方法,即使它们并不是真正必要的(即我有一个基类来放置它们,并且我可以控制代码) )。

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

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

接口上的扩展方法非常很好,允许简单的方法链接等。我更愿意看到扩展方法调用,而不是像 Java 中那样的显式静态调用:

// Explicit helper method call
Collections.sort(collection);
// With extensions, this could be:
collection.sort();

不过有一些缺点:

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

但总的来说,我认为它们很可爱——只要适度使用,它们就能发挥重要作用。 如果没有它们,我当然不想使用 LINQ。

No, it's not a can of worms worth avoiding. It can make life a lot simpler, and IMO it allows you to separate out the core operations of the type from "helper" methods which can be implemented solely from those core operations. I've been tempted to use extensions methods in that way even when they're not really necessary (i.e. I've had a base class to put them in, and I'm in control of the code).

You need to understand that extension methods are just a way of making static method calls look like instance methods, when the appropriate namespace containing the extension methods is imported. In particular:

  • It doesn't add methods to a type, so it's not making standard libraries "non-standard". It just means that if you've got the extension methods available, you may be able to work with those libraries more easily.
  • There is an explicit relationship defined with extension methods: the extension method declares which type it is "extending" with the type of the first parameter. It's not composition or aggregation, but I don't see why that's a problem. If something is useful and doesn't actually make the design worse, does it matter whether it's part of traditional OO?
  • Yes, extension methods are portable between projects. As always, there are rules for what happens if two extension methods with the same name are visible at the same time.

Extension methods over interfaces are very nice, allowing simple method chaining etc. I would far rather see an extension method call than an explicit static call as in Java:

// Explicit helper method call
Collections.sort(collection);
// With extensions, this could be:
collection.sort();

A few downsides though:

  • It doesn't allow derived types to override the implementation in a more efficient way. Of course the derived type can declare a method with the same signature, but that'll only get used if the compile-time type is appropriate.
  • The way that extension methods are discovered in C# is a pain. I've ranted about this elsewhere...
  • Extension methods aren't available when using dynamic typing.

Overall though, I think they're lovely - and when used in moderation, they can make all the difference. I certainly wouldn't want to use LINQ without them.

对风讲故事 2024-08-06 03:47:05

您可以关闭闹钟。 扩展方法仅对语法产生非常微小的差异,除此之外没有任何其他差异。 而不是写:

SomeClass.SomeStaticMethod(someVar, someArg);

你写:

someVar.SomeStaticMethod(someArg);

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

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

更新

扩展方法是否“假装施展魔法”? 仅当您认为交换某些语法很神奇时!

更有可能的是,您只是习惯了以某种方式书写的东西,因此看到它们“倒着”书写会让您感到惊讶。 但从其他语言的角度来看,现在这是正确的做法。

某些语言允许任何非实例方法有选择地在方法名称之前使用第一个参数编写,如果您省略了点,那么这是支持中缀运算符(如 a + b)的巧妙方法,而无需做任何特别的事情。

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

trimmed = string.Trim(str);

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

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

注意事项

我不想给人留下扩展方法的所有使用都有意义的印象。 例如,是否有充分的理由在 object 上创建扩展方法,或在无约束类型 T 上创建通用扩展方法? 这些东西几乎可以在任何上下文中由智能感知建议,并且会变成类似语言扩展的东西,例如

然后还有关于扩展方法是否应该允许其第一个参数为 null 的争论。 就我个人而言,我认为只要方法的名称能够明确这一点就可以了,因此我认为 string.IsNullOrEmpty 作为扩展方法就可以了。 但其他人对此更加激进,而我不在乎,所以我会采取“在罗马时”的态度。 这是另一个示例:

public static void DisposeIfNotNull(this IDisposable d)
{
    if (d != null)
        d.Dispose();
}

名称清楚地表明它包含空检查(这就是它的全部要点)。

You can switch the alarm bells off. Extension methods make only a very tiny difference to the syntax, and nothing else. Instead of writing:

SomeClass.SomeStaticMethod(someVar, someArg);

You write:

someVar.SomeStaticMethod(someArg);

It simply swaps the ordering and eliminates the class name qualifier. Otherwise, it's identical. The value of this is that you can type a variable name and the IDE can suggest useful methods to you in the intellisense feature, and you can read the code from left to right, so "nested" function calls become far more readable.

All your concerns apply equally to static methods - and are not therefore worth worrying about.

Update

Do extension methods "pretend to do magic"? Only if you think swapping some syntax around is magic!

It's more likely that you are just accustomed to seeing things written in a certain way, and so it comes as a surprise to you to see them written "backwards". But from the perspective of other languages, now it's the right way around.

Some languages allow any non-instance method to be optionally written with the first argument before the method name, and if you leave out the dot then it's a neat way of supporting infix operators like a + b without having to do anything special.

An instance method is one way to enable this syntax, but if you were happy to do without it, then to be truly consistent, why not require instance methods to be called like this?

trimmed = string.Trim(str);

This is similar to how F# does it. After all, an instance method of string can be viewed as having a first parameter of type string. You don't normally think of it as a parameter, but it works much the same, and there are advantages to uniform syntax.

An instance method has access to the private members of the class it is defined in, whereas an extension method does not. But then, an instance method in a derived class does not have access to the private data of the base class. And in any case, why should the caller care about this?

Caveats

I wouldn't want to give the impression that all uses of extension methods make sense. For example, are there any good reasons to make an extension method on object, or a generic extension method on the unconstrained type T? Such things would be suggested by intellisense in almost any context, and would become something like language extensions, e.g. With.

Then there's the debate over whether an extension method should allow its first argument to be null. Personally I think it's fine as long as the method's name makes this clear, so I think string.IsNullOrEmpty would be fine as an extension method. But others are more militant about it, and I don't care, so I'd take a "when in Rome" attitude. Here's another example:

public static void DisposeIfNotNull(this IDisposable d)
{
    if (d != null)
        d.Dispose();
}

The name makes it clear that it incorporates a null check (that's the whole point of it).

荒芜了季节 2024-08-06 03:47:05

我将其视为实现装饰器模式的便捷方法。 扩展方法允许您通过向类型添加方法来扩展类型。 当然,如果您的扩展类位于可以共享的单独程序集中,那么它们可以在项目之间移植。

I look at it as a convenient way to implement the decorator pattern. Extension methods let you extend a type by adding methods to it. Of course they are portable between projects if you have your extension classes in a separate assembly which you can share.

新人笑 2024-08-06 03:47:05

我想针对你所说的一点提出一些问题。 扩展方法不允许您向现有类添加方法。 它们允许您呈现向现有类添加方法的外观。 这种区别很微妙,但非常重要。

现在要反驳你提出的观点

  1. 我不相信这是真的。 图书馆的标准化没有任何改变。 使用您的扩展方法的人们将看到库的增强。 它是否会改变库的标准很大程度上取决于您在方法中所做的事情。

  2. 这只是一种用方法增强类型而不改变其原始契约的方法。

  3. 我不确定你所说的可移植是什么意思,但它可能高度依赖于你的实现。 如果存在两个同名的扩展方法,它们将进行重载解析,并适当地引发错误。

使用和定义扩展方法只是静态方法的语法糖。 它允许我使用特定于我的问题域的方法来自定义类型,以解决缺少更好的词的问题,或者只是原始框架中缺少的东西(例如 IEnumerable.ForEach )。

我认为它们的邪恶性没有任何令人信服的论据,因此我将继续使用它们,因为我发现它们很有成效。

I wanted to raise a bit of an issue with a point you said. Extension methods do not allow you to add methods to an existing class. They allow you to give the appearance of adding a method to an existing class. The distinction is subtle but very significant.

Now to go against the points you made

  1. I don't believe this is true. Nothing has changed about the standarization of the library. People using your extension methods will see an augmentation to the library. Whether or not it changes the standard of the library is highly dependent on what you do in your methods.

  2. It's simply a way of augmenting a type with methods without changing it's original contract.

  3. I'm not sure what you mean by portable but it's likely highly dependent on your implementation. If two extension methods exist with the same name they will go through overload resolution and errors will be raised appropriately.

Using and defining extension methods are simply syntactic sugar for static methods. It allows me to customize a type for lack of a better word with methods that are specific to my problem domain or are simply something that is otherwise missing from the original framework (IEnumerable<T>.ForEach for example).

I don't find any argument of their evilness to be compelling and hence I will keep using them because I find them productive.

奢欲 2024-08-06 03:47:05

其他人已经指出了这样一个事实,扩展方法不会更改原始类型,因此只需添加一点:请记住,扩展方法仅在导入定义名称空间后才可见。 因此,如果您向字符串“添加”一些方法,它们只有在您明确请求这些方法时才可用。 换句话说,扩展方法的范围是可以控制的。 也许并不完美,但至少当有人将它们添加到项目中时,它们不会突然出现。

Others have already pointed out the fact, that extension methods do not change the original type, so just to add a bit: Keep in mind that the extension methods are only visible once you import the defining namespace. So if you "add" some methods to string, they only become available when you explicitly request these. In other words the scope of extension methods can be controlled. Maybe not perfectly, but at least they don't just appear out of nowhere when someone adds them to a project.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文