Question:
As I understand it, methods of the class itself take precedence over extension methods.
A natural question arises:
And what happens if I spawn extension methods, and then after some time a new version of the library comes out, where exactly the same method is defined in the class. It turns out that it will be a priority and can work a little differently – everything will break down.
So extension methods are a dangerous thing?
Here is an example:
public class Person
{
public void Say()
{
Console.WriteLine("1");
}
}
public static class Test
{
public static void Say(this Person p)
{
Console.WriteLine("2");
}
}
class Program
{
static void Main(string[] args)
{
var t = new Person();
t.Say();
}
}
Let's say we implemented the Say
method as an extension and it outputs 2
to us, then the update of the lib comes and then bam and outputs 1
. The compiler does not issue any warnings, although it issues a warning if we inherit and declare a method with the same signature and it asks to write new
.
Another funny thing is that you can not control the call to a particular extension method if there are intersections between namespaces. In this case, the only option is to call via static, which kills the usability of extensions.
Answer:
So extension methods are a dangerous thing?
Yes, it certainly is. Extension methods are a crude hack that violates the principles of object-oriented programming, and in addition to the problem of uncontrolled hiding by members of the class being extended, they also suffer from the problem of conflicts with themselves (because different libraries can create extension methods for the same class). They should not be overused.
The Framework Design Guidelines explicitly do not recommend this:
AVOID frivolously defining extension methods, especially on types you don't own.
They also mention two cases where the use of extension methods is justified:
-
Adding some functionality to all types that implement a particular interface. In this case, the danger is somewhat lower, because interfaces usually do not add new methods (since this would require rewriting the code of a bunch of classes that already implement this interface). This is exactly how LINQ works.
-
Methods bound to types defined in your own code. Let's say you create an extension method to the
String
class that takes an argument of typePerson
, defined in your project. In this case, the described conflict will also not exist, because even if MS adds a new method to theString
class, it will definitely not use the class from your code in any way.
Unfortunately, following these recommendations still does not completely solve the problem, since conflicts remain between the extension methods themselves from different libraries. This is exactly the problem faced by the developers of the MoreLinq library.
Of course, the Framework Design Guidelines are guidelines for developers of public library APIs, not any code in applications. But this does not change the essence much, since one should strive to make any code reusable. Projects tend to grow, split, etc.; some code that was previously used only in one project will later want to be moved to a shared library. Therefore, these recommendations can largely be extended to application code.
Update. C# 8.0 introduced a new feature – default interface methods . Thanks to it, libraries can add new methods to the interface without breaking backward compatibility. Therefore, for interface extension methods, there is now also the problem of potential hiding by a method of an extensible interface.
On the other hand, this innovation reduces the need to write extension methods to add functionality to interfaces, since that functionality can now be implemented as default methods.