C#使用通用强制转换调用阴影方法

时间:2014-08-11 14:09:51

标签: c# generics shadow

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

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");
    }
}

我的控制台输出是:

您好

hello2

我调试了它,检查了所有内容并找不到错误。在两种情况下,输出应为hello2。我错过了一些明显的东西吗?或者这只是不起作用?

3 个答案:

答案 0 :(得分:3)

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

以这种方式思考,用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();
}

答案 1 :(得分:3)

当方法声明为virtual时,将调用的方法的分辨率将延迟到运行时,并取决于运行时对象的类型。来自C# spec

  

在虚方法调用中,实例的运行时类型   调用发生的位置决定了实际的方法   要调用的实现。

C# spec开始,虚拟方法的解析如下:

  

对于在类中声明或继承的每个虚方法,都存在   存在关于方法的最派生的实现   那个班。虚拟方法M的最多派生实现   对R类的尊重如下确定:

     
      
  • 如果R包含M的引入虚拟声明,那么这是M的最多派生实现。
  •   
  • 否则,如果R包含覆盖M ,则这是M的派生最多的实现。
  •   
  • 否则,关于R的M的最派生实现与关于的M的最派生实现相同   R的直接基类。
  •   

所以当你写:

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

编译器知道h2的类型为hello或派生,而write是一个虚拟方法,其分辨率将推迟到运行时。

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

这也是为什么如果您使用覆盖而不是hello2上声明写入,则同一程序的输出将为 hello2 两种情况。

修改

您似乎想编写一种能够在writehello中调用hello2的方法,即使该方法已在hello2中使用新运算符声明

您可以编写使用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

答案 2 :(得分:0)

您在方法中使用的write方法

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

是在write类型上定义的hello方法(使用intellisense检查)。因此,当您实际使用类型hello2的特定T类型调用方法时,如果键入write,它仍然是hello方法。由于您使用new运算符在类型write中定义hello2方法,因此不会调用此方法。

如果您明确地将其转换为在方法中键入hello2 ,而不是尚未知道类型{{},则只能使用hello2类型的方法1}}。