簡體   English   中英

覆蓋LINQ擴展方法

[英]Overriding LINQ 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?");
        }
    }
}

有沒有辦法從第二種情況獲得行為,沒有明確的演員? 可以避免這個問題嗎?

擴展方法是靜態方法,您不能覆蓋靜態方法。 也不能用靜態/擴展方法“覆蓋”實際的實例方法。

您必須明確使用優化的擴展。 或者隱式地引用您自己的擴展名的命名空間而不是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<T>進行優化。 除此之外,它無法知道底層具體類型是否實際上支持索引訪問。 你必須以同樣的方式做到這一點; 創建另一個界面,如IHorn或其他東西,在你的擴展中,檢查IBoat是否也實現了IHorn ,就像Enumerable類在這里一樣。

如果您不控制IBoat類或擴展方法的代碼,那么您運氣不好。 如果這樣做,那么使用多接口繼承,顯式類型檢查或動態代碼,這些都是您的選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM