2010-04-24 137 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类或扩展方法的代码,那么您运气不好。如果你这样做,那么使用多接口继承,显式类型检查或动态代码,那些是你的选择。