覆盖LINQ扩展方法

Rub*_*sch 5 .net c# linq mono extension-methods

有没有办法覆盖扩展方法(提供更好的实现),而不必显式地转换它们?我正在实现一种能够比默认扩展方法更有效地处理某些操作的数据类型,但我想保持IEnumerable的通用性.这样就可以传递任何IEnumerable,但是当我的类被传入时,它应该更有效率.

作为玩具示例,请考虑以下事项:

// 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?");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法从第二种情况获得行为,没有明确的演员?可以避免这个问题吗?

Aar*_*ght 15

扩展方法是静态方法,您不能覆盖静态方法.也不能用静态/扩展方法"覆盖"实际的实例方法.

您必须明确使用优化的扩展.或者隐式地引用您自己的扩展名的命名空间而不是System.Linq.

或者显式检查扩展中的类型,并根据运行时类型调用正确的类型.

这似乎是一个比扩展方法更适合继承的问题.如果您想要基于运行时类型的不同功能,那么将基本方法设置为虚拟并在派生类中覆盖它.

我看到在扩展方法的这个方面存在很多困惑.你必须明白它们不是mixins,它们实际上并没有被注入到课堂中.它们只是编译器识别的语法糖,并且"允许"您执行它,就像它是常规实例方法一样.想象一下,它不是一个扩展方法,而只是一个静态方法:

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

你将如何从IBoat实现中"覆盖"这个方法?你不能.您可以做的唯一事情是将类型检查放入此静态方法,或者使用dynamicC#4中的块或早期版本中的Reflection 编写一些动态方法调用代码.

为了使这更清楚,请看一下System.Linq.EnumerableReflector中的类代码:

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];
    }
// ...
}
Run Code Online (Sandbox Code Playgroud)

这是.NET Framework中的核心扩展方法之一.它允许通过显式检查参数是否实现来进行优化IList<T>.除此之外,它无法知道底层具体类型是否实际上支持索引访问.你必须以同样的方式做到这一点; 创建另一个类似IHorn或类似的接口,并在您的扩展中,检查是否IBoat也实现IHorn,与Enumerable类在这里相同.

如果你不控制IBoat类或扩展方法的代码,那么你运气不好.如果这样做,那么使用多接口继承,显式类型检查或动态代码,这些都是您的选择.