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

发布于 2024-08-26 16:24:15 字数 188 浏览 12 评论 0原文

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

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

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

I find them a very natural way to extend existing classes, especially when you just need to "spot-weld" some functionality onto an existing class.

Microsoft says, "In general, we recommend that you implement extension methods sparingly and only when you have to." And yet extension methods form the foundation of Linq; in fact, Linq was the reason extension methods were created.

Are there specific design criteria where using extension methods are perferred over inheritance or composition? Under what criteria are they discouraged?

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

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

发布评论

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

评论(7

云柯 2024-09-02 16:24:15

我们向 C# MVP 展示了提议的新语言功能(至少是那些有一定机会面世的功能)以获得早期反馈。我们经常收到的关于许多功能的一些反馈是“嗯,会根据该功能的设计原则明智地使用该功能,但是我的傻瓜同事会对这个东西发疯,写出大量无法维护、无法理解的代码,我将在接下来的十年里陷入调试困境,所以尽管我想要它,但请永远不要实现这个功能!”

虽然为了喜剧效果我有些夸张,但这是我们非常认真对待的事情;我们希望新功能的设计能够鼓励它们的使用,但阻止它们的滥用。

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

我个人特别关心您在“流畅”编程中看到的“可爱”扩展方法。像呸克之类的东西

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

。 “for”循环在 C# 中是普遍可以理解和惯用的; 别表现得可爱

We show proposed new language features (at least the ones that have a halfway decent chance of seeing the light of day) to the C# MVPs for early feedback. Some feedback we get very often on many features is "well I would use this feature judiciously and in accordance with the design principles of the feature, but my goofball coworkers are going to go crazy nuts with this thing and write up a mass of unmaintainable, non-understandable code that I'm going to get stuck with debugging for the next ten years, so as much as I want it, please never implement this feature!"

Though I am exaggerating somewhat for comedic effect, this is something we take very seriously; we want the design of new features to encourage their use but discourage their misuse.

We worried that extension methods would be used to, for example, indiscriminantly extend "object" arbitrarily and thereby create big balls of loosely typed mud that would be hard to understand, debug and maintain.

I'm personally in particular concerned about "cute" extension methods that you see in "fluent" programming. Stuff like

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

Yuck. "for" loops are universally understandable and idiomatic in C#; don't get cute.

快乐很简单 2024-09-02 16:24:15

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

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

 myObject.Foo();

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

对于扩展方法,情况并非如此。该方法几乎可以在任何地方针对任何类型定义(您可以在 System.Object 上定义扩展方法,尽管这是一个坏主意......)。这确实增加了代码的维护成本,因为它降低了可发现性。

任何时候添加不明显的代码,或者甚至降低实现的“明显性”,都会在长期可维护性方面产生成本。

The problem is that Extension methods aren't necessarily clear.

When you see something like this in code:

 myObject.Foo();

The natural instinct is that Foo() is a method defined on myObject's class, or a parent class of myObject's class.

With Extension methods, that's just not true. The method could be defined nearly ANYWHERE, for nearly any type (you can define an extension method on System.Object, although it's a bad idea...). This really increases the maintenance cost of your code, since it reduces discoverability.

Any time you add code that is non-obvious, or even reduces the "obviousness" of the implementation, there is a cost involved in terms of long term maintainability.

黎夕旧梦 2024-09-02 16:24:15

过度使用扩展方法是不好的,就像过度使用重载运算符不好一样。让我解释一下。

我们先看一下运算符重载的例子。当你看到代码时:

var result = myFoo + myBar;

你会希望 myFoomyBar 是数字类型,并且 operator + 正在执行加法。这是合乎逻辑的、直观的、容易理解的。但是,一旦运算符重载出现,您就无法再确定 myFoo + myBar 实际上在做什么 - + 运算符可能会被重载为意味着任何。您不能只阅读代码并弄清楚发生了什么,而不必阅读表达式中涉及的类型的所有底层代码。现在,operator + 已经对 StringDateTime 等类型进行了重载 - 然而,对于加法有一个直观的解释em> 表示在这些情况下。更重要的是,在这些常见情况下,它为代码增加了很多表达能力。因此,潜在的混乱是值得的。

那么这一切与扩展方法有什么关系?嗯,扩展方法引入了类似的情况。如果没有扩展方法,当您看到:

var result = myFoo.DoSomething();

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

请不要误解我的意思。运算符重载和扩展方法都是有用且强大的语言功能。但请记住 - 能力越大,责任越大。当这些功能提高实现的清晰度或能力时,您应该使用它们。如果你开始不加区别地使用它们,它们将会给你想要创建的东西增加混乱、复杂性,甚至可能的缺陷。

Excessive use of extension methods is bad for the same reason that excessive use of overloaded operators is bad. Let me explain.

Let's look at the operator overloading example first. When you see the code:

var result = myFoo + myBar;

you would like to hope that myFoo and myBar are numeric types, and the operator + is performing addition. This is logical, intuitive, easy to understand. But once operator overloading enters the picture, you can no longer be sure of what myFoo + myBar is actually doing - the + operator could be overloaded to mean anything. You can't just read the code and figure out what's happening without having to read all of the code underlying the types involved in the expression. Now, operator + is already overloaded for types like String or DateTime - however, there is an intuitive interpretation to what addition means in those cases. More importantly, in those common cases it adds a lot of expressive power to the code. So it's worth the potential confusion.

So what does all this have to do with extension methods? Well, extension methods introduce a similar situation. Without extension methods, when you see:

var result = myFoo.DoSomething();

you can assume that DoSomething() is either a method of myFoo or one of it's base classes. This is simple, easy to understand, intuitive even. But with extension methods, DoSomething() could be defined anywhere - and worse, the definition depends on the set of using statements in the code file and bring in potentially many classes into the mix, any one of which could host the implementation of DoSomething().

Now don't get me wrong. Both operator overloading and extension methods are useful and powerful language features. But remember - with great power comes great responsibility. You should use these features when they improve the clarity or capability of the implementation. If you start using them indiscriminately, they will add confusion, complexity, and possibly defects to what you are trying to create.

苹果你个爱泡泡 2024-09-02 16:24:15

我遵循扩展方法和辅助类的非常简单的规则。

每当我有一个可以归入静态帮助器类的方法时,我都会在一般范围内权衡它的有用性,我真的希望共享该方法吗?含义是否清晰并且对其他人有用?

如果我对这个问题的回答是肯定的,我将把它创建为一个扩展方法。我有一个共享库,其中包含 1 个命名空间中的所有扩展方法,每个类文件定义为 TypeNameExtension,因此该类内部的期望内容变得非常清楚,以便您可以轻松找到代码。

如果我质疑是否需要将辅助方法作为全局可访问的用法,我将将该方法声明为私有静态并将其保留在所属类中。

I follow a very simple rule with extension methods and helper classes.

Anytime I have a method that could be relegated to a static helper class I will weigh the usefulness of it in a general scope, do I really want this method to be shared? Is the meaning clear and useful to others?

If I can answer yes to this question I will create it to be an extension method. Where I have a shared library that among other things contains all of my Extension methods in 1 namespace with each class file defined as TypeNameExtension so it becomes very clear what to expect is inside that class so that you can easily locate the code.

If I question the need for a helper method as a globally accessible usage I will declare the method private static and leave it inside the owning class.

一个人练习一个人 2024-09-02 16:24:15

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

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

When Borland invented them (Delphi Class Helpers) the guidance already was: "should be used in exceptional situations only".

They have become a lot more accepted (as part of LINQ), but they remain an odd way add functionality. I think Reed has a good answer on why.

岁吢 2024-09-02 16:24:15

我有一个不断增长的扩展方法库,这些方法对 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

I have a growing library of extension methods that operate on BCL classes for simplifying common problems, that look confusing at first glance. Frankly, I think Extension Methods make things a lot more readable. For instance, take an if statement testing to see if a number is between two numbers:

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

What if right is less than left on accident? And exactly what does that do? When the variables are simply like x and left, then its easy to undertand. But what if they are properties on a long class? The if statement can get pretty big, which obfuscates the rather simple thing you are trying to do.

So I wrote this:

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);
}

Now I can improve my if statement into this:

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

Is this "extraordinary"? I don't think so... but I do think it's a significant improvement on readability, and it allows some extra checking on the test inputs for safety.

The danger I think Microsoft wants to avoid here is people writing a ton of Extension Methods that redefine Equals and ToString.

情场扛把子 2024-09-02 16:24:15

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

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

对于动态语言,范例是不同的,但我相信您正在谈论 C# 和 VB.net。

Extension methods can be thought of as Aspect Oriented. I believe Microsoft is saying, if you have the source code, you should be altering the original class. It has to do with clarity and ease of maintenance. The reason extension methods were needed is that they extend functionality of existing objects. In the case of LINQ, if you dig into how it works, objects can be created with a variety of types that cannot be known ahead of time. Using extension methods allows some behaviors to be added.

For example, it makes sense to extend .NET framework objects because you don't have the source, but may have some behavior to add to an object. Extending the string, DateTime and FrameworkElement classes is acceptable. It may also be acceptable to create extension methods for classes that cross application boundaries. Say, if you wanted to share a DTO class and have some specific UI behaviour, create that extension method in the UI only.

With dynamic languages, the paradigm is different, but I believe you're talking about C# and VB.net.

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