8

I recently saw a code base which had a data class Address defined somewhere and then in a different place:

fun Address.toAnschrift() = let { address ->
    Anschrift().apply {
        // mapping code here...
    }
}

I found it confusing to not have this method on address directly. Are there any established patterns or best practices when to use extension methods? Or does the book on that still have to be written?

Note that despite the Kotlin syntax in my example, I am interested in general best practice as it would also apply to C# or other languages with those capabilities.

  • 4
    With "Anschrift" being a german version of "address", this seems to be a country-specific conversion of a country-neutral address type. Wanting to keep the country-specific code somewhere else makes sense. This is not an answer to your actual question, but it explains why they did it in this example. – R. Schmitz May 31 '18 at 13:48
  • 1
    »Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type« docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…. »For a class library that you implemented, you shouldn't use extension methods ... If you want to add significant functionality to a library for which you own the source code, you should follow the standard .NET Framework guidelines for assembly versioning« tl;dr if you own the code, no need for extension methods. – Thomas Junk Jun 1 '18 at 15:49
13

Extension methods do not provide something that can't be done in other ways. They are syntactical sugar to make development nicer, without providing an actual technical benefit.


Extension methods are relatively new. Standards and conventions are usually decided by popular opinion (plus some long term experience on what ends up being a bad idea), and I don't think we're at the point where we can draw a definitive line that everyone agrees with.

However, I can see several arguments, based on things I've heard from coworkers.

1. They are usually replacing static methods.

Extension methods are most commonly based on things that would otherwise have been static methods, not class methods. They look like class methods, but they aren't really.

Extension methods simply shouldn't be considered an alternative for class methods; but rather as a way to give otherwise syntactically ugle static methods a "class methody" look and feel.

2. When your intended method is specific to a consuming project but not the source library.

Just because you develop both the library and the consumer doesn't mean that you're willing to put the logic wherever it fits.

For example:

  • You have a PersonDto class coming from your DataLayer project.
  • Your WebUI project wants to be able to convert a PersonDto to a PersonViewModel.

You can't (and wouldn't want to) add this conversion method to your DataLayer project. Since this method is scoped to the WebUI project, it should live inside that project.

An extension method allows you to have this method globally accessible within your project (and possible consumers of your project), without requiring the DataLayer library to implement it.

3. If you think data classes should only contain data.

For example, your Person class contains the properties for a person. But you want to be able to have a a few methods that format some of the data:

public class Person
{
    //The properties...

    public SecurityQuestionAnswers GetSecurityAnswers()
    {
        return new SecurityQuestionAnswers()
        {
            MothersMaidenName = this.Mother.MaidenName,
            FirstPetsName = this.Pets.OrderbyDescending(x => x.Date).FirstOrDefault()?.Name
        };
    }
}

I've seen plenty of coworkers who hate the idea of mixing data and logic in a class. I don't quite agree with simple things like formatting data, but I do concede that in the above example, it feels dirty for Person to have to depend on SecurityQuestionAnswers.

Putting this method in an extension method prevents sullying the otherwise pure data class.

Note that this argument is one of style. This is similar to partial classes. There is little to no technical benefit to doing so, but it allows you to separate code into multiple files if you deem it cleaner (e.g. if many people use the class but don't care about your additional custom methods).

4. Helper methods

In most projects, I tend to end up with helper classes. These are static classes that usually provide some collection of methods for easy formatting.

E.g. one company I worked at had a particular datetime format they wanted to use. Rather than having to paste the format string all over the place, or make the format string a global variable, I opted for a DateTime extension method:

public static string ToCompanyFormat(this DateTime dt)
{
    return dt.ToString("...");
} 

Is that better than a normal static helper method? I think so. It cleans up the syntax. Instead of

DateTimeHelper.ToCompanyFormat(myObj.CreatedOn);

I could do:

myObj.CreatedOn.ToCompanyFormat();

This is similar to a fluent version of the same syntax. I like it more, even though there is no technical benefit from having one over the other.


All of these arguments are subjective. None of them cover a case that could otherwise never be covered.

  • Every extension method can easily be rewritten to a normal static method.
  • Code can be separated in files using partial classes, even if extension methods didn't exist.

But then again, we can take your assertion that they are never necessary to the extreme:

  • Why do we need class methods, if we can always make static methods with a name that clearly indicates that it's for a particular class?

The answer to this question, and yours, remains the same:

  • Nicer syntax
  • Increased readability and less code pedantry (code will get to the point in less characters)
  • Intellisense provides contextually meaningful results (extension methods are only shown on the correct type. Static classes are usable everywhere).

One particular extra mention though:

  • Extension methods allow for a method chaining coding style; which has become more popular lately, as opposed to the more vanilla method wrapping syntax. Similar syntactical preferences can be seen in LINQ's method syntax.
  • While method chaining can be done by using class methods as well; keep in mind that this doesn't quite apply to static methods. Extension methods are most commonly based on things that would otherwise have been static methods, not class methods.
  • 5
    Point 2. is the one I find most convincing, and the reason I've done this myself. Sometimes you have a class that's widely used across your whole codebase, and a method that seems as if it "should" be on the class except that it involves some project or third-party library that you want to encapsulate. Extension methods for your own classes are a logical way to handle this. – Astrid May 31 '18 at 14:18
  • @Astrid: I think that's the most technical reason to do it; yes. – Flater May 31 '18 at 14:23
  • Very nice answer. One C# particular use case is that interfaces don’t have behavior. Extension methods allow us to give them methods. – RubberDuck May 31 '18 at 15:58
  • What do you mean by "technical benefits?" Things like performance? Code readability is equally important, if not more so. – Robert Harvey Jun 1 '18 at 15:14
  • @RobertHarvey By technical, I mean anything that's not just syntactical sugar. E.g. spreading a partial class over many files in the same assembly has no technical advantage. Spreading a class and its extensionmethods over different assemblies does. Code readability of course still matters, but not from a technical point of view. – Flater Jun 1 '18 at 15:16
5

I agree with Flater that there hasn't been enough time for convention to crystallize, but we do have the example of the .NET Framework Class Libraries themselves. Consider that when LINQ methods were added to .NET, they weren't added directly onto IEnumerable, but as extension methods.

What can we take from this? LINQ is

  1. a cohesive set of functionality that isn't always needed,
  2. is intended to be used in method-chaining style (e.g. A.B().C() instead of C(B(A))), and
  3. doesn't require access to private state of the object

If you have all three of those features, extension methods make a lot of sense. In fact, I would argue that if a set of methods has feature #3 and either one of the first two features, you should consider making them extension methods.

Extension methods:

  1. Allow you to avoid polluting the core API of a class,
  2. Not only allow method-chaining style, but allow it to more flexibly handle null values since an extension method called from a null value simply receives the null as its first argument,
  3. Prevent you from accidentally accessing/modifying private/protected state in a method that shouldn't have access to it

Extension methods have one more benefit: they can still be used as a static method, and can be passed directly as a value to a higher order function. So if MyMethod is an extension method, instead of say, myList.Select(x => x.MyMethod()), you could simply use myList.Select(MyMethod). I find that nicer.

  • 4
    The reason LINQ was implemented as a set of extension methods is that it's intended to be a common set of operations over an interface and you can't put implementations on interfaces. It's not really anything to do with your points (1) and (2). This is in contrast to Java's solution to the same problem, which is to allow implementations in interface definitions (which opens the door to a world of interface abuse). – Ant P May 31 '18 at 17:05
  • "and you can't put implementations on interfaces". Not yet, but that statement will no longer be true when C# 8 hits our machines :) – David Arno Jun 1 '18 at 13:23
  • @DavidArno really? I've been out of the C# world for a while. That's a shame. Seems like a step backwards to me... – Ant P Jun 13 '18 at 13:59
4

The driving force behind extension methods is the ability to add functionality you need to all classes or interfaces of a certain type regardless of whether they are sealed or in another assembly. You can see evidence of this with LINQ code, adding functions to all IEnumerable<T> objects, whether they were lists or stacks, etc. The concept was to future proof the functions so that if you came up with your own IEnumerable<T> you could automatically use LINQ with it.

That said, there language feature is new enough that you don't have any guidelines on when to use or not use them. However, they do enable a number of things previously not possible:

  • Fluent APIs (see Fluent Assertions for an example of how to add fluent APIs to just about any object)
  • Integrating features (NetCore Asp.NET MVC uses extension methods to wire in dependencies and features in the startup code)
  • Localizing the impact of a feature (your example in the opening statement)

To be fair, only time will tell how far is too far, or at what point extension methods become a hindrance rather than a boon. As mentioned in another answer, there is nothing in an extension method that can't be done with a static method. However, when used judiciously they can make the code easier to read.

What about intentionally using extensions methods in the same assembly?

I think there is value in having core functionality well tested and trusted, and then not messing with it until you absolutely have to. New code that depends on it, but needs it to provide functionality solely for the benefit of the new code can use Extension Methods to add any features to support the new code. Those functions are only of interest for the new code, and the scope of the extension methods are limited to the namespace they are declared within.

I wouldn't blindly rule out the use of extension methods, but I would consider whether the new functionality should really be added to the existing core code that is well tested.

  • 1
    +1 for fluent apis. – Robert Harvey Jun 1 '18 at 15:11
  • @RobertHarvey, My last paragraph under the heading for "within the same assembly"? If you have the source code for that assembly, you can do whatever you want with it, but once it's compiled it's sealed. – Berin Loritsch Jun 1 '18 at 15:42
  • Removed my comment, as I was apparently not communicating my point effectively. – Robert Harvey Jun 1 '18 at 16:03
1

Are there any established patterns or best practices when to use extension methods?

One of the most common reasons for using extension methods is as a means of "extending, not modifying" interfaces. Rather than have IFoo and IFoo2, where the latter introduces a new method, Bar, Bar is added to IFoo via an extension method.

One of the reasons Linq uses extension methods for Where, Select etc is in order to allow users to define their own versions of these methods, which are then used by the Linq query syntax too.

Beyond that, The approach that I use, which seems to fit what I commonly see in the .NET framework at least is:

  1. Put methods directly related to the class within the class itself,
  2. Put all methods that aren't related to the core functionality of the class in extension methods.

So if I have an Option<T>, I'd put things like HasValue, Value, ==, etc within the type itself. But something like a Map function, which converts the monad from T1 to T2, I'd put in an extension method:

public static Option<TOutput> Map<TInput, TOutput>(this Option<TInput> input, 
                                                   Func<TInput, TOutput> f) => ...
  • 1
    One of the reasons Linq uses extension methods for Where, Select etc is in order to allow users to define their own versions of these methods, which are then used by the Linq query syntax too. -- Are you sure about that? Linq syntax makes extensive use of lambda expressions (i.e. first-class functions), so that it's not necessary to write your own versions of Select and Where. I know of no cases where people are rolling their own versions of these functions. – Robert Harvey Jun 1 '18 at 15:08
  • That said, I find your answer the most compelling of all the answers so far. Extension methods are a great way to evolve interfaces, which is precisely why they were created for Linq. – Robert Harvey Jun 1 '18 at 15:09
  • @RobertHarvey, you don't have to, but you can. – David Arno Jun 1 '18 at 15:18
  • @RobertHarvey If you switch the if (!predicate(item)) in that code to if (predicate(item)), the results change as it's using my version of Where. – David Arno Jun 1 '18 at 15:18
  • Yes, I understand. But nobody would ever do this; they would simply change the predicate. That's the whole point of having predicates: change the condition without changing the function. – Robert Harvey Jun 1 '18 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.