扩展方法重写

本文关键字:重写 方法 扩展 | 更新日期: 2023-09-27 18:04:00

让我们假设我有两个类。

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

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

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

我知道它们不是虚拟函数,也不是像它们那样的行为。然而,我真的很奇怪编译器在这种情况下的行为。

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

扩展方法重写

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

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

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


现在:

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

如果没有铸造,这是不可能的。该扩展绑定到要扩展的确切类型,因此您需要有确切的类型才能调用它的扩展。

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

正如您所说,没有办法使扩展成为虚拟的。

你可以通过静态方法自己实现整个虚拟方法模式,但我有一种强烈的感觉,这对你来说没有任何实际用处,这更像是一个有趣的理论解决方案,因为所涉及的工作对这么简单的事情来说是令人望而却步的。

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

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){...}

我最近也遇到了类似的问题。我有一个包含类层次结构的第三方类库(比如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作为其参数。