使用泛型强制转换调用阴影方法

本文关键字:调用 阴影 方法 转换 泛型 | 更新日期: 2023-09-27 18:17:23

我正在尝试编写一个方法,将对象转换为泛型类型以执行特定的影子方法。这是我的测试代码:

class Program
{
    static void Main(string[] args)
    {
        hello2 h2 = new hello2();
        test(h2);
        Console.ReadLine();
    }
    static void test(hello h)
    {
        h.write2<hello2>();
    }
}
class hello
{
    public virtual void write()
    {
        Console.WriteLine("hello");
    }
    public void write2<T>() where T : hello
    {
        T h2 = (T)this;
        hello2 h21 = (hello2)this;
        h2.write();
        h21.write();
    }
}
class hello2 : hello
{
    public new void write()
    {
        Console.WriteLine("hello2");
    }
}

My Console Output is:

你好

hello2

我调试了它,检查了所有内容,但没有发现错误。在这两种情况下,输出都应该是hello2。我是不是漏掉了什么明显的东西?或者这只是不起作用?

使用泛型强制转换调用阴影方法

您缺少的是在hello的编译时而不是在使用write2时选择调用的方法。所以在编译时,编译器只知道Thello类型或其他派生类,所以它选择阴影方法,因为这是它在编译时唯一知道的方法。

这样想,将每个T替换为:右侧的任何内容。这是编译器看到的信息,并使用该信息做出选择。

//What the complier sees when you compile even if you pass in hello2 as T.
public void write2<T>() where T : hello
{
    hello h2 = (hello)this;
    hello2 h21 = (hello2)this;
    h2.write();
    h21.write();
}

当方法声明为virtual时,将被调用的方法的解析延迟到运行时,并取决于运行时对象的类型。来自c#规范:

在虚方法调用中,实例的运行时类型调用发生的地方决定了实际的方法

在c#规范中,虚方法的解析如下:

对于在类中声明或继承的每个虚方法,有存在该方法的最派生的实现这类。虚方法M的最派生实现

  • 如果R包含M的引入虚声明,则这是M的最派生的实现。
  • 否则,如果R包含 M的重写,则这是M的最派生实现。
  • 否则,M相对于R的最派生实现与M相对于的最派生实现相同r的直接基类。

所以当你写:

T h2 = (T)this;
h2.write();

编译器知道h2是hello或派生类型,而write是一个虚方法,其解析将延迟到运行时。

在运行时,即使类的类型是hello2,解析也会选择类hello中的方法。这是因为hello2类中的write方法不包含override关键字,因此在解析虚拟调用时不会考虑它。(因此,在hello2上调用write的唯一方法是确保编译器知道实例的类型是hello2)

这也是为什么如果你在hello2上用override而不是new声明write,那么在这两种情况下,同一个程序的输出将是hello2

编辑

似乎您想编写一个能够在hellohello2中调用write的方法,即使该方法已在hello2中使用new操作符声明。

您可以编写一个使用dynamic变量的扩展方法。例如,创建以下扩展方法:

public static class HelloExtensions
{
    public static void writeDynamic<T>(this T h) where T: hello
    {
        dynamic hdynamic = h;
        hdynamic.write();
    }
}
然后,您可以编写以下测试程序,该程序将该扩展方法与原始类一起使用:
class Program
{
    static void Main(string[] args)
    {
        testDynamic(new hello());
        testDynamic(new hello2());
        Console.ReadLine();
    }
    static void testDynamic(hello h)
    {
        h.writeDynamic();
    }
}

输出将是:

hello
hello2

您在方法中使用的write方法

public void write2<T>() where T : hello {
    T h2 = (T)this;
    hello2 h21 = (hello2)this;
    h2.write();  // <--- this one
    h21.write();
}

是在hello类型上定义的write方法(使用智能感知检查它)。因此,当您实际调用类型为hello2的特定T类型的方法时,如果类型为hello,它仍然是write方法。由于您使用new操作符在类型hello2中定义write方法,因此不会调用该方法。

如果在方法中显式地将类型转换为hello2 ,则只能使用类型为hello2的方法,而不能使用类型未知的T