Cred că utilizarea judicioasă a metodelor de extensie pune interfețele într-o poziție mai echivalentă cu clasele de bază (abstractă).
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.
Ca exemplu concret, prima versiune a unei biblioteci ar putea defini o interfață cum ar fi:
public interface INode {
INode Root { get; }
List GetChildren( );
}
Odată ce biblioteca a lansat, nu putem modifica interfața fără a întrerupe utilizatorii actuali. În schimb, în următoarea versiune ar trebui să definim o nouă interfață pentru a adăuga funcționalitate suplimentară:
public interface IChildNode : INode {
INode Parent { get; }
}
Cu toate acestea, numai utilizatorii noii biblioteci vor putea să implementeze noua interfață. Pentru a lucra cu codul vechi, trebuie să adaptăm vechea implementare, pe care o metodă de extindere o poate gestiona frumos:
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 );
}
}
Acum, toți utilizatorii noii biblioteci pot trata în mod identic atât implementările moștenite, cât și cele moderne.
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.
În schimb, metodele de extensie pot fi folosite pentru a furniza implementări triviale de suprasarcină:
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