📜 ⬆️ ⬇️

Extension methods: stop the madness!

Long time ago, when computers were large, programmers were bearded, and code was procedural, an idyll reigned on Earth. Programmers wrote simple and understandable code, without thinking about its conformity to dogmas. And then there was no dogma. Each of these lonely cowboys was a creator in their own world. Each expressed his thought elegantly and succinctly; each line of code was a work of art worthy of unlimited admiration. It could not be otherwise: computing resources were so scarce that it never occurred to anyone to spend them only on making the code look beautiful .

However, as time went on, the invincible machine of technical progress inexorably increased the productivity of iron. One day, people realized that they could write non-optimized code, and they would have nothing for it! Computational capacities have grown so much that the computers didn’t care what to do: the code verified to the last machine instruction, or the code written by the first-year student for the sausage in the test. This was truly a turning point, after which, like mushrooms after rain, new programming concepts and techniques began to appear.

One of these concepts was object-oriented programming. The idea really turned out to be good: with the help of the object model, you can create a clear and orderly structure that strictly describes the rules for the functioning of your system. Perhaps the PLO - one of the few paradigms that brings real benefits due to the complexity of the rules.

Let's look at an elementary example, just to remember what the OOP is (that is, one of its aspects is encapsulation ).
')
public class Cat { public float CuddlynessFactor { get; private set; } public Cat(float cuddlynessFactor) { this.CuddlynessFactor = cuddlynessFactor; } public void Purr() { Console.WriteLine("Purr!"); } public void HitTheWall() { Console.WriteLine("Fu**ing meow!!!"); } } public class Dog { public float TeethSharpness { get; private set; }; public Dog(float teethSharpness) { this.TeethSharpness = teethSharpness; } public void Bark() { Console.WriteLine("Bark!"); } public void Bite(Human target) { target.Leg.DoDamage(DamageLevel.Substantial); } } 


As you can see, we have identified two simplest classes: Cat and Dog. Each of these classes is a representation of an object from real life and has some properties of these objects that are relevant in the context of our system. These objects are able to somehow interact with the environment (methods) and possess some qualities (fields). The essence of encapsulation is that each object contains all the methods and data necessary for its operation. In addition, the objects do not contain methods or properties that are not related to their behavior (still, what kind of dog would think to accelerate the floor and slam into the wall at full speed?).

Now imagine that we want to perform some actions on these objects. For example, we want to make a cat out of a dog. This behavior does not belong to any of the classes listed, as no dog in their right mind will become transmogrified into a cat. For this purpose, we can create a third-party static class containing the desired behavior.

 public static class Transmogrificator { public static Cat DogToCat(Dog dog) { return new Cat(Math.Sqrt(dog.TeethSharpness) * 42); } } 


Voila! Using a new class, we can easily turn a dog into a cat!

 public void Test() { var uberDog = new Dog(float.Infinity); var testHuman = new Human(); uberDog.Bark(); uberDog.Bite(testHuman); var cat = Transmogrificator.DogToCat(dog); cat.HitTheWall(); } 


Sweet! And now we are coming close to the concept of Extension methods, which Microsoft recently introduced into .NET. According to the authors, extension methods should be used where the target class does not have any functionality that it obviously needs (or at least can be part of it). That's what says about extension methods in. and y. MSDN:

In general, you will probably more often than not realize your own.
[...]
In general, we recommend that you implement investment methods sparingly and only when you have to. Whenever possible, it is possible to extend the existing type from the existing type.
[...]
In the case of the use of an extension method, it is possible to make it.


Simply put, the authors advise us to use extension methods only when it is really necessary .

Now let's see what happens if we modify our example by making the DogToCat method an extension method.

 public void Test() { var uberDog = new Dog(float.Infinity); var testHuman = new Human(); uberDog.Bark(); uberDog.Bite(testHuman); var cat = dog.DogToCat(); cat.HitTheWall(); } 


It would seem that nothing terrible happened. But no! Now, while reading the code, the overall impression is that the DogToCat method is an integral part of the Dog type. To understand the trick, you need to wait for IntelliSense hints or go to the immediate implementation of the method. Let's see why this situation is non-kosher:

In principle, one can think of a thousand more arguments against the use of extension methods instead of the usual static classes. However, it seems to me that even these two are enough to cry out: “People! Think again! You are back in the dark days of procedural programming and a jumble of classes! You are mired in dog breeding! Repent, sinners! In the name of Stroustrup, Richter and McConnell! ”

UPD : Apparently, spreading the thought over the tree, I did not clearly express the essence of the article. And the point is this: I urge you not to use Extension methods to extend the functionality of classes that are in the same project . To write an extension for a class in a compiled assembly, to the source code of which you do not have access, is good. Using extensions to create a LINQ-like syntax is very good. But to create classes, and in the same project to attach additional functionality to them using Extensions is very, very bad.

Source: https://habr.com/ru/post/122320/


All Articles