Advertisement
Advertisement


Extension interface patterns


Question

The new extensions in .Net 3.5 allow functionality to be split out from interfaces.

For instance in .Net 2.0

public interface IHaveChildren {
    string ParentType { get; }
    int ParentId { get; }

    List<IChild> GetChildren()
}

Can (in 3.5) become:

public interface IHaveChildren {
    string ParentType { get; }
    int ParentId { get; }
}

public static class HaveChildrenExtension {
    public static List<IChild> GetChildren( this IHaveChildren ) {
        //logic to get children by parent type and id
        //shared for all classes implementing IHaveChildren 
    }
}

This seems to me to be a better mechanism for many interfaces. They no longer need an abstract base to share this code, and functionally the code works the same. This could make the code more maintainable and easier to test.

The only disadvantage being that an abstract bases implementation can be virtual, but can that be worked around (would an instance method hide an extension method with the same name? would it be confusing code to do so?)

Any other reasons not to regularly use this pattern?


Clarification:

Yeah, I see the tendency with extension methods is to end up with them everywhere. I'd be particularly careful having any on .Net value types without a great deal of peer review (I think the only one we have on a string is a .SplitToDictionary() - similar to .Split() but taking a key-value delimiter too)

I think there's a whole best practice debate there ;-)

(Incidentally: DannySmurf, your PM sounds scary.)

I'm specifically asking here about using extension methods where previously we had interface methods.


I'm trying to avoid lots of levels of abstract base classes - the classes implementing these models mostly already have base classes. I think this model could be more maintainable and less overly-coupled than adding further object hierarchies.

Is this what MS has done to IEnumerable and IQueryable for Linq?

2019/01/20
1
25
1/20/2019 1:53:40 PM

Accepted Answer

I think the judicious use of extension methods put interfaces on a more equatable position with (abstract) base classes.


Versioning. One advantage base classes have over interfaces is that you can easily add new virtual members in a later version, whereas adding members to an interface will break implementers built against the old version of the library. Instead, a new version of the interface with the new members needs to be created, and the library will have to work around or limit access to legacy objects only implementing the original interface.

As a concrete example, the first version of a library might define an interface like so:

public interface INode {
  INode Root { get; }
  List<INode> GetChildren( );
}

Once the library has released, we cannot modify the interface without breaking current users. Instead, in the next release we would need to define a new interface to add additional functionalty:

public interface IChildNode : INode {
  INode Parent { get; }
}

However, only users of the new library will be able to implement the new interface. In order to work with legacy code, we need to adapt the old implementation, which an extension method can handle nicely:

public static class NodeExtensions {
  public INode GetParent( this INode node ) {
    // If the node implements the new interface, call it directly.
    var childNode = node as IChildNode;
    if( !object.ReferenceEquals( childNode, null ) )
      return childNode.Parent;

    // Otherwise, fall back on a default implementation.
    return FindParent( node, node.Root );
  }
}

Now all users of the new library can treat both legacy and modern implementations identically.


Overloads. Another area where extension methods can be useful is in providing overloads for interface methods. You might have a method with several parameters to control its action, of which only the first one or two are important in the 90% case. Since C# does not allow setting default values for parameters, users either have to call the fully parameterized method every time, or every implementation must implement the trivial overloads for the core method.

Instead extension methods can be used to provide the trivial overload implementations:

public interface ILongMethod {
  public bool LongMethod( string s, double d, int i, object o, ... );
}

...
public static LongMethodExtensions {
  public bool LongMethod( this ILongMethod lm, string s, double d ) {
    lm.LongMethod( s, d, 0, null );
  }
  ...
}


Please note that both of these cases are written in terms of the operations provided by the interfaces, and involve trivial or well-known default implementations. That said, you can only inherit from a class once, and the targeted use of extension methods can provide a valuable way to deal with some of the niceties provided by base classes that interfaces lack :)


Edit: A related post by Joe Duffy: Extension methods as default interface method implementations

2016/04/25
9
4/25/2016 8:51:32 AM

Extension methods should be used as just that: extensions. Any crucial structure/design related code or non-trivial operation should be put in an object that is composed into/inherited from a class or interface.

Once another object tries to use the extended one, they won't see the extensions and might have to reimplement/re-reference them again.

The traditional wisdom is that Extension methods should only be used for:

  • utility classes, as Vaibhav mentioned
  • extending sealed 3rd party APIs
2008/08/12

I think the best thing that extension methods replace are all those utility classes that you find in every project.

At least for now, I feel that any other use of Extension methods would cause confusion in the workplace.

My two bits.

2008/08/11

I see separating the domain/model and UI/view functionality using extension methods as a good thing, especially since they can reside in separate namespaces.

For example:

namespace Model
{
    class Person
    {
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string Surname { get; set; }
    }
}

namespace View
{
    static class PersonExtensions
    {
        public static string FullName(this Model.Person p)
        {
            return p.Title + " " + p.FirstName + " " + p.Surname;
        }

        public static string FormalName(this Model.Person p)
        {
            return p.Title + " " + p.FirstName[0] + ". " + p.Surname;
        }
    }
}

This way extension methods can be used similarly to XAML data templates. You can't access private/protected members of the class but it allows the data abstraction to be maintained without excessive code duplication throughout the application.

2008/08/17

There is nothing wrong with extending interfaces, in fact that is how LINQ works to add the extension methods to the collection classes.

That being said, you really should only do this in the case where you need to provide the same functionality across all classes that implement that interface and that functionality is not (and probably should not be) part of the "official" implementation of any derived classes. Extending an interface is also good if it is just impractical to write an extension method for every possible derived type that requires the new functionality.

2008/08/17

I see a lot of people advocating using a base class to share common functionality. Be careful with this - you should favor composition over inheritance. Inheritance should only be used for polymorphism, when it makes sense from a modelling point of view. It is not a good tool for code reuse.

As for the question: Be ware of the limitations when doing this - for example in the code shown, using an extension method to implement GetChildren effectively 'seals' this implementation and doesn't allow any IHaveChildren impl to provide its own if needed. If this is OK, then I dont mind the extension method approach that much. It is not set in stone, and can usually be easily refactored when more flexibility is needed later.

For greater flexibility, using the strategy pattern may be preferable. Something like:

public interface IHaveChildren 
{
    string ParentType { get; }
    int ParentId { get; }
}

public interface IChildIterator
{
    IEnumerable<IChild> GetChildren();
}

public void DefaultChildIterator : IChildIterator
{
    private readonly IHaveChildren _parent;

    public DefaultChildIterator(IHaveChildren parent)
    {
        _parent = parent; 
    }

    public IEnumerable<IChild> GetChildren() 
    { 
        // default child iterator impl
    }
}

public class Node : IHaveChildren, IChildIterator
{ 
    // *snip*

    public IEnumerable<IChild> GetChildren()
    {
        return new DefaultChildIterator(this).GetChildren();
    }
}
2008/08/18

Source: https://stackoverflow.com/questions/8042
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]