开发者

What is the motivation behind "Use Extension Methods Sparingly?"

开发者 https://www.devze.com 2022-12-24 02:02 出处:网络
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.

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 u开发者_开发问答sing extension methods are perferred over inheritance or composition? Under what criteria are they discouraged?


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.


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.


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.


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.


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消