简体   繁体   English

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

[英]C# Call shadow method with generic cast

I'm trying to write a method which casts an object to a generic type to execute a specific shadow method. 我正在尝试编写一个方法,将对象强制转换为泛型类型以执行特定的阴影方法。 This is my test code: 这是我的测试代码:

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: 我的控制台输出是:

hello 你好

hello2 hello2

I debugged it, checked everything and couldn't find a mistake. 我调试了它,检查了一切,找不到错误。 The Output should be hello2 in both cases. 在两种情况下,输出应为hello2。 Am I missing something obvious here? 我错过了一些明显的东西吗? Or is this just not working? 或者这只是不起作用?

The thing you are missing is the method that is called is chosen at the compile time of hello not at the use of write2 . 你缺少的是调用的方法是在hello的编译时选择的,而不是使用write2 So at compile time all the compiler knows is that T is of type hello or some other derived class so it chooses the shadowed method as that is the only method it knows about at compile time. 所以在编译时,所有编译器都知道Thello类型或其他派生类,所以它选择了shadowed方法,因为这是它在编译时知道的唯一方法。

Think of it this way, replace every T with whatever is on the right side of the : . 可以这样想,用以下任何东西替换每一个T : . This is what the compiler sees and uses that information to make its choices. 这是编译器看到并使用该信息进行选择的内容。

//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();
}

When the method is declared as virtual the resolution of the method that will be called is deferred until runtime and depends on the type of the object at runtime. 当方法声明为virtual ,将调用的方法的解析延迟到运行时,并取决于运行时对象的类型。 From the C# spec : 来自C#规范

In a virtual method invocation, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. 在虚方法调用中,进行该调用的实例的运行时类型决定了要调用的实际方法实现。

From the C# spec , the resolution of a virtual method is as follows: C#规范来看,虚方法的解析如下:

For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. 对于在类中声明或继承的每个虚方法,都存在关于该类的方法的最派生实现。 The most derived implementation of a virtual method M with respect to a class R is determined as follows: 关于类R的虚拟方法M的最派生实现确定如下:

  • If R contains the introducing virtual declaration of M, then this is the most derived implementation of M. 如果R包含M的引入虚拟声明,那么这是M的最派生实现。
  • Otherwise, if R contains an override of M , then this is the most derived implementation of M. 否则,如果R包含M的覆盖 ,那么这是M的派生最多的实现。
  • Otherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R. 否则,关于R的M的最派生实现与关于R的直接基类的M的最派生实现相同。

So when you write: 所以当你写:

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

The compiler knows h2 is of type hello or derived, and that write is a virtual method whose resolution will be deferred until runtime. 编译器知道h2的类型为hello或derived,而write是一个虚方法,其分辨率将延迟到运行时。

At runtime, the resolution will chose the method in class hello even though the class is of type hello2 . 在运行时,解析将在类hello选择方法,即使该类的类型为hello2 This is because the write method in class hello2 does not contains the override keyword and won't be considered when resolving the virtual call. 这是因为类hello2中的write方法不包含override关键字,在解析虚拟调用时不会考虑。 (So the only way to call write on hello2 is by making sure the compiler knows the instance is of type hello2 ) (因此,在hello2上调用write的唯一方法是确保编译器知道实例的类型为hello2

This is also why if you declare write on hello2 with override instead of new the output of the same program would be hello2 in both cases. 这也是为什么如果你使用override而不是new声明在hello2上写入,两种情况下同一程序的输出都是hello2

Edit 编辑

It seems you want to write a method that is able to call write in hello or hello2 even though the method has been declared with the new operator in hello2 . 看来你要编写能够调用的方法writehellohello2尽管该方法已被宣布与新的运营商hello2

You could write a extension method that uses a dynamic variable. 您可以编写一个使用dynamic变量的扩展方法。 For example create the following extension method: 例如,创建以下扩展方法:

public static class HelloExtensions
{
    public static void writeDynamic<T>(this T h) where T: hello
    {
        dynamic hdynamic = h;
        hdynamic.write();
    }
}

Then you could write the following test program that uses that extension method with your original classes: 然后你可以编写以下测试程序,将该扩展方法与原始类一起使用:

class Program
{
    static void Main(string[] args)
    {
        testDynamic(new hello());
        testDynamic(new hello2());
        Console.ReadLine();
    }

    static void testDynamic(hello h)
    {
        h.writeDynamic();
    }
}

The output will be: 输出将是:

hello
hello2

The write method that you are using in your method 您在方法中使用的write方法

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

is the write method defined on the hello type (check it using intellisense). hello类型定义的write方法(使用intellisense检查)。 Therefore when you are actually calling the method with a specific T type of type hello2 it is still the write method if type hello . 因此,当您实际使用特定类型为hello2 T类型调用该方法时,如果键入hello它仍然是write方法。 And since you are defining the write method in type hello2 using the new operator, this method is not called. 由于您使用new运算符在hello2类型中定义了write方法,因此不会调用此方法。

You can only use methods of type hello2 if you explicitly cast it to type hello2 in your method , and not to not-yet-known-type T . 如果在方法中显式地将其hello2转换为hello2 则只能使用hello2类型的方法 ,而不能使用尚未知类型的T

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM