【问题标题】:Overriding LINQ extension methods覆盖 LINQ 扩展方法
【发布时间】:2010-04-24 19:38:13
【问题描述】:

有没有一种方法可以覆盖扩展方法(提供更好的实现),而无需显式地强制转换为它们?我正在实现一种能够比默认扩展方法更有效地处理某些操作的数据类型,但我想保持 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?");
        }
    }
}

有没有办法从第二种情况中获得行为,而无需显式转换?这个问题可以避免吗?

【问题讨论】:

  • 我认为你应该更有效地在你的类中实现你的 IEnumerable,而不是改变扩展方法。

标签: c# .net linq mono extension-methods


【解决方案1】:

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

您必须明确使用优化的扩展程序。或者通过引用您自己的扩展名而不是 System.Linq 来隐式引用。

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

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

我看到很多关于扩展方法的这方面的困惑。您必须了解它们不是 mixin,它们实际上并没有被注入到类中。它们只是编译器识别并“允许”您执行它的语法糖,就好像它是常规实例方法一样。想象一下,它不是扩展方法,而是静态方法:

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

您将如何从IBoat 实现中“覆盖”此方法?你不能。您唯一能做的就是在这个静态方法中加入类型检查,或者编写一些动态方法调用代码,或者使用 C# 4 中的 dynamic 块或早期版本中的反射。

为了更清楚地说明这一点,请查看来自 Reflector 的 System.Linq.Enumerable 类中的这段代码:

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];
    }
// ...
}

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多