【问题标题】:C# Call shadow method with generic castC# 使用泛型转换调用影子方法
【发布时间】:2014-08-11 14:09:51
【问题描述】:

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

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

我的控制台输出是:

你好

你好2

我调试了它,检查了一切,没有发现错误。在这两种情况下,输出都应该是 hello2。我在这里遗漏了一些明显的东西吗?或者这只是不起作用?

【问题讨论】:

  • 使用override 而不是new
  • @Selman22 这并不能解释为什么目前它没有输出“hello2”。
  • 这只是一个示例。在我的情况下,我不能使用覆盖。

标签: c# generics shadow


【解决方案1】:

您缺少的是在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();
}

【讨论】:

    【解决方案2】:

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

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

    C# spec,一个虚方法的解析如下:

    对于在类中声明或继承的每个虚方法,都有 存在关于该方法的最衍生的实现 那堂课。虚拟方法 M 的最派生实现 关于类 R 的确定如下:

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

    所以当你写的时候:

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

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

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

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

    编辑

    您似乎想要编写一个能够在hellohello2 中调用write 的方法,即使该方法已使用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
    

    【讨论】:

      【解决方案3】:

      您在方法中使用的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。 p>

      【讨论】:

        猜你喜欢
        • 2016-07-06
        • 2011-04-23
        • 2015-12-08
        • 2012-08-04
        • 1970-01-01
        • 1970-01-01
        • 2021-03-17
        • 2011-05-18
        • 1970-01-01
        相关资源
        最近更新 更多