Remplacement des méthodes d’extension LINQ

Existe-t-il un moyen de remplacer les méthodes d’extension (pour une meilleure implémentation), sans avoir à les utiliser explicitement? J’implémente un type de données capable de gérer certaines opérations plus efficacement que les méthodes d’extension par défaut, mais j’aimerais conserver la généralité de IEnumerable. Ainsi, n’importe quel IEnumerable peut être passé, mais quand ma classe est passée, ça devrait être plus efficace.

Comme exemple de jouet, considérons ce qui suit:

// Comstack: dmcs -out:test.exe test.cs using System; namespace Test { public interface IBoat { void Float (); } public class NiceBoat : IBoat { public void Float () { Console.WriteLine ("NiceBoat floating!"); } } public class NicerBoat : IBoat { public void Float () { Console.WriteLine ("NicerBoat floating!"); } public void BlowHorn () { Console.WriteLine ("NicerBoat: TOOOOOT!"); } } public static class BoatExtensions { public static void BlowHorn (this IBoat boat) { Console.WriteLine ("Patched on horn for {0}: TWEET", boat.GetType().Name); } } public class TestApp { static void Main (ssortingng [] args) { IBoat niceboat = new NiceBoat (); IBoat nicerboat = new NicerBoat (); Console.WriteLine ("## Both should float:"); niceboat.Float (); nicerboat.Float (); // Output: // NiceBoat floating! // NicerBoat floating! Console.WriteLine (); Console.WriteLine ("## One has an awesome horn:"); niceboat.BlowHorn (); nicerboat.BlowHorn (); // Output: // Patched on horn for NiceBoat: TWEET // Patched on horn for NicerBoat: TWEET Console.WriteLine (); Console.WriteLine ("## That didn't work, but it does when we cast:"); (niceboat as NiceBoat).BlowHorn (); (nicerboat as NicerBoat).BlowHorn (); // Output: // Patched on horn for NiceBoat: TWEET // NicerBoat: TOOOOOT! Console.WriteLine (); Console.WriteLine ("## Problem is: I don't always know the type of the objects."); Console.WriteLine ("## How can I make it use the class objects when the are"); Console.WriteLine ("## implemented and extension methods when they are not,"); Console.WriteLine ("## without having to explicitely cast?"); } } } 

Existe-t-il un moyen d’obtenir le comportement du second cas, sans casting explicite? Ce problème peut-il être évité?

Les méthodes d’extension sont des méthodes statiques et vous ne pouvez pas remplacer une méthode statique. Vous ne pouvez pas non plus “écraser” une méthode d’instance réelle avec une méthode statique / d’extension.

Vous devrez utiliser explicitement votre extension optimisée. Ou implicitement en référençant l’espace de noms de votre propre extension au lieu de System.Linq .

Ou vérifiez explicitement le type dans votre extension et appelez le bon en fonction du type d’exécution.

Cela semble être un problème mieux adapté à l’inheritance que les méthodes d’extension. Si vous souhaitez une fonctionnalité différente en fonction du type d’exécution, définissez la méthode de base comme virtuelle et remplacez-la dans les classes dérivées.

Je vois beaucoup de confusion sur cet aspect des méthodes d’extension. Vous devez comprendre qu’ils ne sont pas mixins, ils ne sont pas réellement injectés dans la classe. Ce sont simplement des sucres syntaxiques que le compilateur reconnaît et vous “permet” de les exécuter comme s’il s’agissait d’une méthode d’instance régulière. Imaginez que ce ne soit pas une méthode d’extension, mais simplement une méthode statique:

 public static void BlowHorn (IBoat boat) { Console.WriteLine ("Patched on horn for {0}: TWEET", boat.GetType().Name); } 

Comment “substitueriez-vous” cette méthode depuis l’implémentation d’ IBoat ? Tu ne peux pas. La seule chose que vous pouvez faire est de mettre le contrôle de type dans cette méthode statique ou d’écrire du code d’invocation de méthode dynamic, en utilisant un bloc dynamic en C # 4 ou Reflection dans des versions antérieures.

Pour rendre cela encore plus clair, jetez un oeil à ce code de la classe System.Linq.Enumerable de Reflector:

 public static TSource ElementAt(this IEnumerable source, int index) { TSource current; if (source == null) { throw Error.ArgumentNull("source"); } IList list = source as IList; if (list != null) { return list[index]; } // ... } 

C’est l’une des méthodes d’extension principales du .NET Framework. Il permet une optimisation en vérifiant explicitement si le paramètre implémente IList . En dehors de cela, il n’a aucun moyen de savoir si le type concret sous-jacent prend réellement en charge l’access indexé. Vous devriez le faire de la même manière; créez une autre interface comme IHorn ou quelque chose du IHorn et, dans votre extension, vérifiez si IBoat implémente également IHorn , comme dans la classe Enumerable .

Si vous ne contrôlez pas le code des classes IBoat ou des méthodes d’extension, vous avez de la chance. Si vous le faites, utilisez un inheritance à plusieurs interfaces, une vérification de type explicite ou un code dynamic, telles sont vos options.