5

C# extension methods have seen a rise in usage over recent years. The offical microsoft guidelines on usage state: "In general, we recommend that you implement extension methods sparingly and only when you have to" The extension method guidelines.

On the other hand Microsoft are now using them heavily in dot net core (see the microsoft extensions name space). This is particularly prevalent is asp core where the initializaion of the IServiceCollection is implemented in extension methods, for example see The service collection service extensions. Dot net core tutorials has an article listing this as a design pattern suggesting that it is best practice. Quite a few popular nuget packages also use this method to initialise services: Swagger is configured this way microsoft docs as well as mediatr their implementation

Should the guidelines be updated or should this practice be avoided? If the guidelines are to be updated what should they be?

Adrian
  • 167
  • 4
  • 2
    think someone else asked this question a while back. Avoid was my vote – Ewan Jul 13 '19 at 06:24
  • @Ewan I couldn't find anything quite as specific regarding the transition Microsoft are making. Avoid is also my vote – Adrian Jul 13 '19 at 06:28
  • 1
    https://softwareengineering.stackexchange.com/questions/382198/should-net-core-class-libraries-register-their-own-implementations/382201#382201 – Ewan Jul 13 '19 at 06:36
  • @Ewan thanks. I would like to see some more general discussion on this so i will leave this up for now – Adrian Jul 13 '19 at 06:45
  • 1
    I agree it is an interesting question. but you might find it gets closed as a duplicate. I mean I could simply copy paste in my answer. Which you seem to agree with and would possibly accept. What can we say other than "its logically bad practice, but MS encourage it. probably due to some guy in .net core liking the idea" – Ewan Jul 13 '19 at 07:04
  • 4
    Could not find a duplicate, only [this related question](https://softwareengineering.stackexchange.com/questions/371868/when-to-write-extension-methods-for-your-own-classes).It is surely an interesting question, however, I don't think there is a "right" or "wrong" answer to this. I would actually close this question either as "too broad" or "primarily opionated". It is something you could discuss with someone like Eric Lippert, but unfortunately, the SE sites are not for discussions. "Quora" might be a better place for this. – Doc Brown Jul 13 '19 at 07:28
  • `... and only when you have to`, in case of `IServiceCollection` this is exactly a case. You not own `IServiceCollection` to add new methods there, but with extension method you can wrap all required registrations under descriptive name. – Fabio Jul 13 '19 at 09:31
  • Few links which might help to understand Extension methods https://stackoverflow.com/questions/2770333/can-extension-methods-be-applied-to-interfaces https://qawithexperts.com/article/c-sharp/extension-methods-in-c-explanation-with-example/225 – Vikas Lalwani Oct 07 '20 at 12:33

2 Answers2

7

Extension methods are just syntactic sugar for ordinary static method calls.

Extension methods make possible the ability to "spot-weld" methods onto existing types, without requiring inheritance, composition, weaving or any other language mechanisms. But they're just a proxy for an ordinary method call; they don't participate in the design of the class, nor do they add any new functionality that the class doesn't already have.

What an extension method does is turn this:

Manager.Foo(bar);

into this:

bar.Foo();

And that's all it really does.

It's arguably prettier (which is no small thing), but really, is there any other compelling reason to use extension methods?

Yes: to enable Method Chaining.

var productList = products
   .Where(prod => prod.StateOfManufacture = "CA")
   .OrderBy(prod => prod.Category)
   .ThenByDescending(prod => prod.UnitPrice)
   .Select(prod => new 
        { Name = prod.ProductName, Category = prod.Category, Price = prod.UnitPrice });

This would be very tedious without extension methods.

The advice "use extension methods sparingly, and only when you have to" is not actionable. Entire libraries and frameworks have been built using extension methods, and you never "have to."

So is it a "best practice?" My answer to that is "does it most effectively meet your specific needs?"

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • The method chaining is the primary benefit that I see them offering. In those cases it does make the code far cleaner than using the underlying static implementations. – Adrian Jul 13 '19 at 23:43
  • @AdrianBlackburn: It can also be useful for reducing interface proliferation as, rather than creating a new interface for a single new capability, you can simply attach it using an extension method. – Robert Harvey Jul 14 '19 at 17:47
7

The goal of extension methods is specifically to be able to extend either classes of other assemblies or interfaces. The recommendation to use them sparingly is right, for several reasons:

  • Extension methods, when used too much, are polluting IntelliSense. This could be extremely annoying when (1) the extension methods were created in a core namespace which is using-included in nearly every file of the project, and when (2) they are extending some very general interfaces or classes, such as object.

    An example of such pollution is NSubstitute. IntelliSense becomes nearly useless in a test project which includes NSubstitute, because there are so many items which are automatically suggested for everything, that the only way to know the members of a specific class or interface would be to go see its documentation, which is a bit disappointing.

  • People who abuse extension methods could in many cases use elementary inheritance, or, in more esoteric situations, patterns such as the decorator pattern.

    A basic example I've seen recently. The code was dealing with geometrical shapes, and, among others, had to convert from meters to inches. The developer simply put two extension methods, ToInches and ToMeters for int, two other ones for double, etc. Instead, he could make an effort to make dedicated classes which encompass the value and the metric system being used, and allow conversions to other systems.

  • Extension methods make unit testing more difficult than it needs to be, as would any public static method anyway, as opposed to an instance method of an object injected at runtime, with the possibility to replace it by a mock or a stub.

  • By extending code which doesn't belong to you, you put yourself at risk of the author of the code adding the method which has the same name as your extension method, but which has a different implementation. This alone should encourage you to use extension methods sparingly, or at least limit yourself to interfaces only.

    As an example, a few years ago, a fellow developer told me a story. Their codebase had a magic method called public static string ToJson(this object x) which would, as you may imagine, return the JSON representation of an object. Nice and all, until one of the classes of a third party was updated with an addition of an instance ToJson method. It took about a week to the team to figure out why is the serialization rules suddenly changed for some of the objects, breaking the application which was consuming the service.

There are cases where extension methods are extremely valuable. LINQ is such case. But developers should be careful to limit themselves to cases similar to LINQ, instead of creating as many extension methods as they can.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • Static methods can be injected with dependencies at runtime (including objects); that's what *parameters* are for. Your objection is to *static state,* not inversion of control. – Robert Harvey Jul 13 '19 at 19:03
  • The last point is the biggest issue I see with extension methods. Regarding namespace pollution extension methods can be used to avoid that, by placing related functionality into a separate name rather than the core interface. – Adrian Jul 13 '19 at 23:49
  • 1
    @Robert Harvey, what Arseni is talking about is not unit testing a static method, in which case what you’re saying makes sense. What he is referring to is unit testing a method that itself makes use of a static method from another class. The _difficulty_ with unit testing such methods as Arseni rightfully claims, arises due to the fact that static methods cannot be mocked easily. Moq for one is certainly unable to do it. You need third party tools such as Typemock. – Ash Jul 15 '19 at 09:34
  • @Ash: So unit test the static method separately, to prove that it works. Then there is no need to mock it. – Robert Harvey Jul 15 '19 at 12:48
  • 1
    @Robert Harvey, I don’t think you follow. We are not interested in unit testing the static method here, there are no issues with that, as you’ve described. The issue here is unit testing methods that themselves call other static methods. Because these static methods cannot be mocked, the System Under Test is not being tested in isolation and therefore by definition, the test is not a unit test. – Ash Jul 15 '19 at 23:18
  • @Ash: I've heard that argument before, but I don't find it compelling. If the problem is that a method being called is uproven, the way you solve that problem is by testing the method. This is no different than if you called untested non-static methods. You don't need to mock a static method to test it, because you're not going to alter static state, are you? Most static methods are free of side-effects, if they are written properly, which means it becomes a simple "pass arguments, assert the expected result" test. – Robert Harvey Jul 16 '19 at 00:34
  • @Ash: See also https://softwareengineering.stackexchange.com/a/5963 – Robert Harvey Jul 16 '19 at 00:41
  • @RobertHarvey So as to not cause a tangential drift, I've commented on the answer you've linked to above. – Ash Jul 16 '19 at 03:17
  • FWIW I'm with @RobertHarvey. If your static methods are [pure](https://en.wikipedia.org/wiki/Pure_function) then no mocking should be necessary, even if they call other static methods (which must also be pure). If you have need for methods that deal with "static" state, they should actually be instanced, but instantiated only once (e.g. by registering with your IoC container as a singleton). If you do it that way then no mocking should ever be necessary. – John Wu Jul 25 '19 at 21:14