• virtual关键字的本质是什么?


    MSDN上对virtual方法的解释:试着翻译如下

    当一个方法声明包含virtual修饰符,这个方法就是虚方法。如果没有virtual修饰符,那么就不是虚方法。

    非虚方法的实现是不变的:不管该方法是被声明该方法的类的实例调用,还是该类的子类所调用,实现的结果都是一样。相比之下,虚方法会在子类中被取代。取代继承的虚方法的过程就是override。

    在一个虚方法的调用中,方法所在的实例的运行时类型决定了实际哪个方法要被实现。在一个非虚方法的调用中,实例的编译时类型(编译时类型与运行时类型的区别)是决定性因素。准确地说,当一个参数列表为A,名叫N的方法在编译时类型C和运行时类型R的实例上调用时(R或者是C或者是C的子类),调用过程如下进行:

    • 首先,重载决议应用在C,N和A上,去从方法集合中选择一个在C中声明并继承的特定的方法M。
    • 然后,如果M是非虚方法,M就被调用。
    • 否则,M是虚方法,R中M派生程度最大的方法的实现被调用。

    对于声明或继承于一个类的每个虚方法,相对于那个类都有一个派生程度最大的实现。对于每一个类R,派生程度最大的虚方法实现如下定义:

    • 如果R包含M的virtual声明,那么这就是M的派生程度最大的实现。
    • 否则,如果R包含M的override,那么,这就是M的派生程度最大的实现。
    • 否则,R的M派生程度最大的实现与R的直接父类的M最大派生程度最大实现相同。
    using System;
    class A
    {
       public void F() { Console.WriteLine("A.F"); }
       public virtual void G() { Console.WriteLine("A.G"); }
    }
    class B: A
    {
       new public void F() { Console.WriteLine("B.F"); }
       public override void G() { Console.WriteLine("B.G"); }
    }
    class Test
    {
       static void Main() {
          B b = new B();
          A a = b;
          a.F();
          b.F();
          a.G();
          b.G();
       }
    }

    结果:

    A.F
    B.F
    B.G
    B.G

     这么一大段,实在很难看懂。最后,根据《你必须知道的.NET》一书的对继承本质的描述,感觉清晰了一些。试着归纳如下:

    对于给定的两个类:

       class A
        {
            public virtual void F()
            {
                Console.WriteLine("A.F");
            }
        }
         class B :A
         {
             public override void F()
             {
                 Console.WriteLine("B.F");
             }
         }

    以及程序:

    A a=new B();
    a.F();//"B.F"

    到底发生了什么呢?

    根据下图:

    显然A是编译时类型,指向了作为运行时类型B的实例。

    当new B()时,首先将B的父类A所有的虚方法都复制了一份,然后和B中自己所有的方法对比,如果B中有override的虚方法,则以子类的方法替换父类的方法,同时添加子类其他的新方法。

    因此,结果是B中的方法被执行。

    另一个例子:

        class Z
        {
            public virtual void F1()
            {
                Console.WriteLine("Z.F");
            }
        }
        class E:Z
        {
            public string type = "eType";
            public override void F1()
            {
                Console.WriteLine(type);
            }
        }
        class F : E
        {
            public string type = "fType";
            public override void F1()
            {
                Console.WriteLine(type);
            }
        }
    E e=new F();
    Console.WriteLine(e.type); e.F1();

    结果出我意外:e.type里是eType,而e.F1()则是fType。

    后者不难理解,因为是override,而前者是因为其为E类型,会首先访问离E类型创建最近的字段或方法。

    最后,仍旧是MSDN上的一个例子

     class A
        {
            public virtual void F() { Console.WriteLine("A.F"); }
        }
        class B : A
        {
            public override void F() { Console.WriteLine("B.F"); }
        }
        class C : B
        {
            new public virtual void F() { Console.WriteLine("C.F"); }
        }
        class D : C
        {
            public override void F() { Console.WriteLine("D.F"); }
        }
                D d = new D();
                A a = d;
                B b = d;
                C c = d;
                a.F();
                b.F();
                c.F();
                d.F();

    结果依次是:

    B.F

    B.F

    D.F

    D.F

    两个D.F的结果好理解,为什么前两个是B.F呢?

    因为d中其实有两个方法,一个是被隐藏的F(),其实现是B.F,另一个是显式的F(),其实现是D.F。根据”new则看类型,override只管新“,因为类型为A和B,那么其结果就为被隐藏的B.F。

  • 相关阅读:
    一个最简单的使用Entity Framework 查询SQL 数据库的例子
    几何算法:点集合构造简单多边形
    序列和集合算法之序列比较
    .Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果
    枚举类型表示组合状态的抽象代数原理
    WCF开发实战系列五:创建WCF客户端程序
    WCF开发实战系列四:使用Windows服务发布WCF服务
    DQN(Deep Reiforcement Learning) 发展历程(五)
    DQN(Deep Reiforcement Learning) 发展历程(四)
    DQN(Deep Reiforcement Learning) 发展历程(三)
  • 原文地址:https://www.cnblogs.com/Benjamin/p/3309856.html
Copyright © 2020-2023  润新知