Domanda

I came accross the following code:

public interface IFoo { }

Make IFoo do something via an extension method:

public static FooExtensions 
{
    public static string Foo(this IFoo foo, string bar)
    {
        // Do work
        return bar;
    }
}

Is this a good idea? Why not use an abstract class with a virtual Foo() instead? IFoo could have some contract methods but a consumer gets the Foo() extension method also.

My question is: When is something like this a good idea?

È stato utile?

Soluzione

The extension method doesn't "make" IFoo do anything. Extension methods just let you extend a type that's closed... it's generally best used in conjunction with code which you don't have the ability to modify, such as framework types or third-party types.

Another possibility is if you have a lot of logic that's absolutely identical across all implementations of your interface, and you want consumers of your interface to have access to that functionality without having to use a base type. Think of LINQ -- it's implemented via extension methods, and you get all the benefits of it just by implementing IEnumerable.

In this case, you're not gaining anything other than an unnecessary layer of indirection. If IFoo should have the ability to do Foo, add Foo to the interface.

Altri suggerimenti

Extention methods is a good idea when you don't want or can't change implementation of the class you are extending. IFoo could be declared in a 3rd party library. Or there might be a lot of code dependent on it so that it is very hard to remake it to an abstract class (maybe some reflection rely on interface).

In general from the usage point of view you should use extention methods when it looks more readable than old-school static methods and anyway you would use static method instead of new class member. When considering extention method vs member, consider static method in helper class vs member and if you select static, then consider if it's better to implement it as extention.

But I often see using extention methods where it really isn't required and usually it makes code less readable. So I wouldn't recommend using them when it's easy and obvious how to avoid them.

When is something like this a good idea?

When you need to teach already existing members which implements this interface with new tricks, like this one from the System.Core assembly:

// System.Linq.Enumerable
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return current;
        }
    }
    throw Error.NoMatch();
}

The reason you might want to do this is when you want an interface to provide a method and the implementation of that method can always be done using the other methods and properties in the interface.

An interface (unlike an abstract base class) give you no way to provide a "default" implementation for a method. By using an extension method you can provide such a method without all implementers of an interface having to provide the same repeated implementation code.

However, a major drawback of this approach is that the method in the extension method is effectively sealed - you cannot implement it differently. Sometimes this is ok, sometimes not - YMMV.

An alternative approach to this is as follows:

  1. Specify your interface as usual, but add the method in question to it.
  2. Provide an abstract base class which provides the default code for the method in question.
  3. Derive from the abstract base class when you want to provide an implementation of this interface.

Another reason you might want to use an extension method is when you either cannot change the existing interface (because it is third-party, for example) or when you don't want to (because it would break existing code).

Extension methods are merely syntax sugar which allow you to change fun(t, x) into t.fun(x). They're useful for discovery (intellisense), or when you want to compose fluent pipelines of functions which follow a "more intuitive" left to right style, rather than right to left. Eg f(x).g(y).h(z) versus h(g(f(x),y),z).

There's not really any downside to using them other than cluttering intellisense.

This is a good idea when you want to give this implementation to any object which implement that interface, regardless of what implementation is that.

An abstract class provide that implementation only to its derived classes.

If that interface is yours, or, you have a single base-abstract class that implements that interface, and it's safe to assume that no implementations which doesn't derive from that class would be in your code - it would be a good idea to implement that functionality in that abstract class (but, you'll have to cast to that abstract class, to use that method, which makes the interface somehow redundant).

However, if you want to provide an implementation (of that method) to all types which implement that interface, regardless of their actual implementation - an extension method would be a better idea. Moreover, a class can only derive from a single class - which means that by deriving from that abstract class, you cannot derive from any other class. So, if you'll have multiple inheritance chains which implements that interface, the only solution to provide that method to all of them (directly), without duplication of code, is via an extension (although there other solution to provider the functionality, but it wouldn't be directly: objWhichImplIFoo.Foo()).

BTW, there is another reason to want an extension: if you want it to be callable from nulls. A declared method will always throw a NullReferenceException if the object is null. Because extensions are actually static methods - they can be called upon nulls:

IFoo foo = null;
var something = foo.GetSomethingOrDefault();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top