2013-03-15 137 views
5

我们假设我有两个类。扩展方法覆盖

public class A {...} 
public class B : A {...} 

我想要实现的是重写这两种类型的扩展函数。

public static void HelperExtension(this A a) {...} 
public static void HelperExtension(this B b) {...} 

我知道他们不是虚拟功能或行为像他们一样。不过,我真的不知道这种情况下的编译器行为

有没有办法在不解析类型的情况下调用类型B的函数?或者任何自动解决建议?

+0

是不是扩展名必须是静态的? – Alex 2013-03-15 14:40:10

+0

是的,我错过了:/ – ozanbora 2013-03-15 14:40:59

+0

如果它是一个B对象,但你有一个对它的引用,你是否需要*它来解析第二个扩展方法?毕竟,调用A引用上的虚拟方法无论如何都将在B对象上解析。你是否控制A和B班?如果是这样,你不应该使用扩展方法,但用普通方法修改类。 – nvoigt 2013-03-15 14:45:02

回答

2

正如你所说,没有办法让虚拟扩展。

你可能implement the entire virtual method pattern yourself through static methods但我有一种强烈的感觉,这对你来说不会有什么实际用处,它更像是一种有趣的理论解决方案,因为所涉及的工作对于这样简单的事情来说是不可接受的。

如果有可能的子类的固定,数量有限,你可以有第一种方法具有这样的:

public static void HelperExtension(this A a) 
{ 
    B b = a as B; 
    if(b != null) 
     HelperExtension(b); 
    else 
     //the rest of the method. 
} 

你可以使用一个Switch甚至Dictionary<Type, Action<A>>如果你有很多子类,但它会很乏味,很难维护,并且不支持在编译时不知道的任意继承者。

另一种选择是在编译时通过使用dynamic基本上利用编译器的功能。我强烈建议尽可能避免它,但在这种特殊情况下,它允许您在A上有一个单独的公共扩展,每个子类型使用一组私有静态方法(使用不同的名称),然后调用一次调度:

public static void HelperExtension(this A a) 
{ 
    ExtenstionImplementation((dynamic)a); 
} 

private static void ExtenstionImplementation(A a){...} 
private static void ExtenstionImplementation(B a){...} 
private static void ExtenstionImplementation(C a){...} 
5

这不是覆盖 - 它是超载,如果有的话。

这很好 - 因为签名是不同的,即使在同一个命名空间中,编译器也不会有任何编译问题。

但是,将调用哪个扩展方法取决于变量的确切类型。


现在:

有没有办法叫B型功能,不解决它的类型?或者任何自动解决建议?

没有铸造这是不可能的。该扩展绑定到正在扩展的确切类型,因此您需要具有确切类型才能在其上调用扩展。

这就是为什么大部分LINQ在IEnumerable<T>上定义。

+2

这不是他要问的。他希望能够做'A a = new B(); a.HelperExtension();'让它调用第二种方法,而不是第一种方法。 – Servy 2013-03-15 14:43:19

+0

@Servy - 错过了。答案已更新。 – Oded 2013-03-15 14:45:46

+0

我期待这样的评论,事情是如果我使用类方法而不是扩展这将是重写。谢谢btw :) – ozanbora 2013-03-15 14:53:55

0

我最近遇到了类似的问题。我有一个包含类的层次结构的第三方类库(比方说IBase,Base和Derived,其中IBase实际上是一个接口)。

public interface IBase {...} 
public class Base : IBase {...} 
public class Derived : Base {...} 

然后,我有一个类,它拥有一个IBase的参考分机。 ext的具体类型可以是Base和Derived。

public class MyClass { 
    // other stuff 
    public IBase ext; 
} 

其实我需要的是一个虚拟的方法AddedMethod()内广积定义,并在每个子类中重写,但是这并不可行。这样,一个可能会倾向于限定含有组重载扩展方法的类:

public static class MyExtensions 
{ 
    public static void AddedMethod(this IBase arg) {...} 
    public static void AddedMethod(this Base arg) {...} 
    public static void AddedMethod(this Derived arg) {...} 
} 

然后,上MyClass的对象调用ext.AddedMethod()。这是行不通的:因为扩展方法是静态绑定的,所以总是调用第一个方法(即AddedMethod(this IBase arg)),而不管实际类型的分机。 这个问题可以通过定义IBASE一个扩展方法可以绕过,然后使用反射来选择一个私有静态方法的正确的实例,其参数类型的实际类型传递给扩展方法匹配:

public static class MyExtensions 
{ 
    // just one extension method 
    public static void AddedMethod(this IBase arg){ 
     // get actual argument type 
     Type itsType = arg.GetType(); 

     // select the proper inner method 
     MethodInfo mi = typeof(MyExtensions).GetMethod("innerAddedMethod", 
      BindingFlags.NonPublic | BindingFlags.Static, 
      null, 
      new Type[] { itsType }, 
      null); 

     // invoke the selected method 
     if (mi != null) { 
      mi.Invoke(null, new object[] { arg }); 
     } 
    } 

    private static void innerAddedMethod(Base arg) { 
     // code for Base type arg 
    } 

    private static void innerAddedMethod(Derived arg) { 
     // code for Derived type arg 
    } 

无论一个新的派生类Derived2应该被添加到IBase层次结构中,我们将不得不简单地向MyExtensions类添加一个重载的InnerAddedMethod(),它将Derived2作为它的参数。