重写 LINQ 扩展方法

发布于 2024-08-30 07:19:53 字数 2378 浏览 7 评论 0原文

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

是否有一种方法可以从第二种情况中获取行为,而无需显式转换?这个问题可以避免吗?

Is there a way to override extension methods (provide a better implementation), without explicitly having to cast to them? I'm implementing a data type that is able to handle certain operations more efficiently than the default extension methods, but I'd like to keep the generality of IEnumerable. That way any IEnumerable can be passed, but when my class is passed in, it should be more efficient.

As a toy example, 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 the behavior from the second case, without explict casting? Can this problem be avoided?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

回忆躺在深渊里 2024-09-06 07:19:53

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

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

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

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

我发现扩展方法在这方面存在很多混乱。你必须明白它们不是 mixins,它们实际上并没有被注入到类中。它们只是编译器识别的语法糖,并“允许”您像常规实例方法一样执行它。想象一下,它不是一个扩展方法,只是一个静态方法:

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

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

为了使这一点更清楚,请查看 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 来进行优化。除此之外,它无法知道底层具体类型是否确实支持索引访问。你必须以同样的方式这样做;创建另一个接口,例如 IHorn 或其他接口,然后在您的扩展中检查 IBoat 是否也实现 IHorn,与 相同Enumerable 类在这里执行。

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

Extension methods are static methods, and you can't override a static method. Nor can you "override" an actual instance method with a static/extension method.

You'll have to use your optimized extension explicitly. Or implicitly by referencing your own extension's namespace instead of System.Linq.

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

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

I see a lot of confusion over this aspect of extension methods. You have to understand that they aren't mixins, they don't actually get injected into the class. They're just syntactic sugar that the compiler recognizes and "allows" you to execute it as if it were a regular instance method. Imagine that it wasn't an extension method, just a static method instead:

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

How would you "override" this method from the IBoat implementation? You can't. The only thing you can do is put type checking into this static method, or write some dynamic method invocation code, either using a dynamic block in C# 4 or Reflection in earlier versions.

To make this even clearer, have a look at this code from the System.Linq.Enumerable class out of 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 core extension methods in the .NET Framework. It allows optimization by explicitly checking if the parameter implements IList<T>. Other than this, it has no way of knowing whether or not the underlying concrete type actually supports indexed access. You'd have to do it this same way; create another interface like IHorn or something, and in your extension, check whether or not the IBoat also implements IHorn, same as the Enumerable class does here.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文