2010-04-24 136 views
4

有沒有一種方法可以覆蓋擴展方法(提供更好的實現),而不必明確地轉換給它們?我正在實現一種數據類型,它能夠比默認的擴展方法更有效地處理某些操作,但我想保持IEnumerable的通用性。這樣任何IEnumerable都可以傳遞,但是當我的類傳入時,它應該更有效率。重寫LINQ擴展方法

當作玩具例子,考慮以下因素:

// 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?"); 
     } 
    } 
} 

是否有一種方式來獲得從第二種情況下的行爲,而無需顯式鑄造?這個問題可以避免嗎?

+0

我認爲你應該在你的類中實現IEnumerable更高效,而不是改變擴展方法。 – 2010-04-24 19:43:06

回答

15

擴展方法是靜態方法,並且不能覆蓋靜態方法。你也不能用靜態/擴展方法「覆蓋」實際的實例方法。

您必須明確使用您的優化擴展。或者通過引用您自己的擴展的名稱空間而不是System.Linq來隱式引用。

或者顯式檢查擴展中的類型,並根據運行時類型調用正確的類型。

這似乎是一個比擴展方法更適合繼承的問題。如果您需要基於運行時類型的不同功能,請將基本方法設爲虛擬並在派生類中重寫它。

我看到很多關於擴展方法的這方面的困惑。你必須明白,他們不是mixin,他們實際上並沒有注入課堂。它們只是語法糖,編譯器可以識別並「允許」執行它,就好像它是常規實例方法一樣。想象一下,這是不是一個擴展方法,而不是隻是一個靜態方法:

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

你會如何「覆蓋」,從IBoat實施這種方法嗎?你不能。你可以做的唯一事情就是將類型檢查放入這個靜態方法中,或者使用C#4中的dynamic塊或早期版本中的Reflection來編寫一些動態方法調用代碼。

爲了使這更清楚,看看這段代碼從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框架的核心擴展方法之一。它允許通過明確檢查參數是否實現IList<T>來進行優化。除此之外,它無法知道基礎具體類型是否實際上支持索引訪問。你必須這樣做,創建另一個界面,如IHorn或其他東西,並在您的分機中,檢查IBoat是否也實現了IHorn,與Enumerable類相同。

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