扩展方法覆盖

时间:2013-03-15 14:38:46

标签: c# extension-methods

我们假设我有两节课。

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

我想要实现的是覆盖两种类型的扩展函数。

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

我知道它们不是虚函数或行为类似于它们。但是我真的很想知道这种情况下编译器的行为

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

3 个答案:

答案 0 :(得分:5)

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

没关系 - 由于签名不同,编译器在编译时也不会有任何问题,即使在相同的命名空间中也是如此。

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


现在:

  

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

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

这就是为什么大多数LINQ都是在IEnumerable<T>上定义的。

答案 1 :(得分: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>>,但它会很繁琐,难以维护,并且不支持在编译时不知道的任意继承者。 / p>

另一种选择是通过使用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){...}

答案 2 :(得分: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;
}

我真正需要的是在IBase中定义并在每个后代类中重写的虚方法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(此IBase arg)),而不管ext的实际类型。 可以通过在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作为其参数。