When Do You Use Extension Methods, Ext. Methods VS. Inheritance

When do you use extension methods, ext. methods vs. inheritance?

Extension methods allow existing classes to be extended without relying on inheritance or having to change the class's source code. This means that if you want to add some methods into the existing String class you can do it quite easily. Here's a couple of rules to consider when deciding on whether or not to use extension methods:

  • Extension methods cannot be used to override existing methods

  • An extension method with the same name and signature as an instance method will not be called

  • The concept of extension methods cannot be applied to fields, properties or events

  • Use extension methods sparingly....overuse can be a bad thing!

Extension methods versus inheritance

Extension methods are useful, but they are harder to discover through the IDE than regular methods, since they are not attached to the original class and there are no clues as to where the code for them might reside. There are some best practice suggestions as to where to put them and how to name them, but these are only guidelines and there is no guarantee that someone will follow them.

Usually you would use extension methods if you are only adding functionality to a well known, well used class or interface such as the .Net base classes, that you don't have access to the code for. Extension methods also have the constraint in that you not only have to have the original assembly, you have to have the assembly with the extension methods in it, which must be understood by consumers of your code.

Using inheritance will allow you to add, remove or override functionality, and ensure that it is always present with the class when you build it.

Extension methods vs overloading

We should use extension methods if we are quite sure that the methods are the same for the all (including potential) interface implementation:

  public interface IFoo {
void Bar(int a);
}

public static class FooExtensions {
public static void Bar(this IFoo foo, int a, int b) {...}
public static void Bar(this IFoo foo, int a, int b, int c) {...}
}

We can implement different Bar(int a) methods

  public MyFoo : IFoo {
void Bar(int a) { /* MyFoo algorithm here */}
}

public MyOtherFoo : IFoo {
void Bar(int a) { /* some other - MyOtherFoo - algorithm here */}
}

But Bar(int a, b) as well as Bar(int a, b, c) are still be the same:

  new MyFoo().Bar(1, 2);      // FooExtensions.Bar(IFoo, int, int) called
new MyOtherFoo().Bar(1, 2); // FooExtensions.Bar(IFoo, int, int) called

If, say, Bar(int a, int b) can vary from implementation to implementation we have have to add it into interface:

  public interface IFoo {
void Bar(int a);
void Bar(int a, int b);
}

...

public MyFoo : IFoo {
void Bar(int a) { /* MyFoo algorithm here */}
void Bar(int a, int b) { /* MyFoo algorithm here */}
}

public MyOtherFoo : IFoo {
void Bar(int a) { /* some other - MyOtherFoo - algorithm here */}
void Bar(int a, int b) { /* some other - MyOtherFoo - algorithm here */}
}

If almost all interface implementations have the same algorithm it'll be boring to put the boiler plate code. However, in C# 8.0 we are going to have a good compromise - default method implmentation, e.g.

  public interface IFoo {
void Bar(int a);
void Bar(int a, int b) {
/* Default code here */
}
}

// uses default code for Bar(int a, int b)
public MyFoo : IFoo {
void Bar(int a) { /* MyFoo algorithm here */}
}

// uses its own code for Bar(int a, int b)
public MyOtherFoo : IFoo {
void Bar(int a) { /* some other - MyOtherFoo - algorithm here */}
void Bar(int a, int b) { /* some other - MyOtherFoo - algorithm here */}
}

Overriding Extension Methods

I agree with Michael. Base classes should contain all base functionality Extension methods should, obviously, extend the base functionality. In dynamic languages like Ruby it is often typical to use extension methods to provide addition functionality instead of using subclasses. Basically, extension methods are there to replacing using subclassses, not to replace using base classes.

The only exception to this that I've seen is if you have multiple type that have different class hierachies (like winform controls), you can make a subclass of each that all implement and interface and then extend that interface, thereby giving "base" functionality to a group of different controls, without extending everything like Control or Object.

Edit: answering your second question

I think the compiler will catch this for you.

Generic type vs Extension method

Those are two entirely different things.

You use generics to provide generic functionality. For repositories, this is often used with a "base entity" class or interface containing properties that all entities implement, like ID:

public interface IEntity
{
int ID { get; set; }
}

public class Client : IEntity
{
public int ID { get; set; }
public string Name { get; set; }
}

public class Repository<T>
where T : IEntity
{
private readonly IQueryable<T> _collection;
public Repository(IQueryable<T> collection)
{
_collection = collection;
}

public T FindByID(int id)
{
return _collection.First(e => e.ID == id);
}
}

You could do that as well with an extension method:

public static T FindByID(this IQueryable<T> collection, int id)
where T : IEntity
{
return collection.First(e => e.ID == id);
}

Without generics, you'd have to implement the repository or the extension method for every type.

Why not use an extension method in this case: you generally only use those when you can't extend the base type. With the repository class you can group operations in one logical class.

See also When do you use extension methods, ext. methods vs. inheritance?, What is cool about generics, why use them?.

With the advent of extension methods, are abstract classes less attractive?

What extension methods lets you do is to focus in on what abstract classes should actually be doing. There's a temptation to implement "utility" code in abstract classes because it will be used by implementers even though it may not be part of the logical inheritance tree. Extension methods let you attach these utility methods to the interface without cluttering up your abstract base classes.

EDIT

Specifically, I would apply these guidelines.

Inheritance

  • DO use inheritance if the behavior is part of the logical behavior of the base class
  • DO NOT use inheritance if the behavior is cross-cutting (applies to things outside of your object hierarchy). These will require duplication.

Utility Classes

  • DO use utility classes (static classes) if the behavior does not logically belong to the class it's acting on
  • DO NOT use utility classes if they modify the internal state of the object it's acting on. State modification should be reserved only for the object implementation hierarchy.

Extension Methods

  • DO use the same decision for extension methods as you would for utility classes when they create less friction. If adopting extension methods feels less natural, don't do it.
  • DO use extension methods to add utility to classes you don't have control over (like string).
  • DO NOT use extension methods when the behavior is consumed by the class it is extending. While it is possible to do it, it feels contrived
  • DO NOT use extension methods just because you can. In fact don't deviate from good old fashioned OOP unless you have to. When you have to, however, extension methods are a relatively uesful and harmless decision to make.

EDIT 2

Thought of another DO for extension methods

DO use extension methods to provide a natural language (internal DSL) for certain implementations. Here's a silly example.

int age = getAge();
if (age.IsDraftAge() && !age.IsLegalDrinkingAge())
{
Console.WriteLine(@"You cannot drink until your birthdate on {0}.
Join the army instead.",
age.GetYearWhenGettingDrunkIsOk());
}


Related Topics



Leave a reply



Submit