Overriding LINQ Extension Methods

Is there a way to override extension methods (provide a better implementation) without explicit casting to them? I implement a data type that is capable of handling certain operations more efficiently than the default extension methods, but I would like to maintain the IEnumerable generality. This way, any IEnumerable can be passed, but when my class is passed, it should be more efficient.

As an example of a toy, consider the following:

// Compile: 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 (string [] 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?"); } } } 

Is there a way to get behavior in the second case without explication? Can this problem be avoided?

+4
source share
1 answer

Extension methods are static methods, and you cannot override a static method. Also, you cannot override the method of the actual instance using the static / extension method.

You will need to use your optimized extension explicitly. Or implicitly, referencing your own namespace instead of System.Linq .

Or explicitly check the type in your extension and call the correct one based on the type of runtime.

This seems like a problem more suitable for inheritance than extension methods. If you want different functionality based on the type of runtime, then make the base method virtual and override it in derived classes.

I see a lot of confusion in this aspect of extension methods. You must understand that they are not mixins, they are not actually introduced to the class. It is just syntactic sugar that the compiler recognizes and "allows" to execute it, as if it were a regular instance method. Imagine that this was not an extension method, but only a static method:

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

How would you "redefine" this method from an IBoat implementation? You can not. The only thing you can do is put type checking in this static method or write the code to call the dynamic method, either using the dynamic block in C # 4, or Reflection in earlier versions.

To make this even clearer, look at this code from the System.Linq.Enumerable class from Reflector:

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

This is one of the main extension methods in the .NET Framework. It allows you to optimize by explicitly checking to see if the parameter implements IList<T> . Other than that, it has no way of knowing if the underlying concrete type really supports indexed access. You must do it the same way; create another interface, for example IHorn or something else, and in your extension check if IBoat IHorn implements the same as the Enumerable class.

If you don't control the code for IBoat classes or extension methods, you're out of luck. If you do, use multiple interface inheritance, explicit type checking, or dynamic code, these are your options.

+15
source

Source: https://habr.com/ru/post/1307890/


All Articles