• review——C# (6)虚方法和覆写方法


    FROM P125

      在上一节中看到,当使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以使基类的引用访问“升至”派生类内。

      可以使用基类引用调用派生类(derived class)的方法,只需满足下列条件:

      □派生类的方法和基类的方法有相同的签名返回类型

      □基类的方法使用virtual标注

      □派生类的方法使用override标注

    使用方法如下例:

    1     class MyBaseClass                                   //基类
    2     {
    3         virtual public void Print();
    4     }
    5     class MyDerivedClass : MyBaseClass          //派生类
    6     {
    7         override public void Print()
    8     }    

    与上一节中不同,使用基类引用调用Print方法时,方法调用被传递到派生类并执行,因为:

    □基类的方法被标记为virtual

    □派生类中有匹配的override方法

    如下图所示,显示了一个从virtual Print方法后面开始,并指向override Print方法的箭头

    具体示例如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         virtual public void Print()
    12         {
    13             Console.WriteLine("This is the base class.");
    14         }
    15     }
    16     class MyDerivedClass : MyBaseClass          //派生类
    17     {
    18         override public void Print()
    19         {
    20             Console.WriteLine("This is the derived class.");
    21         }
    22     }
    23     class Program
    24     {
    25         static void Main(string[] args)
    26         {
    27             MyDerivedClass derived = new MyDerivedClass();
    28             MyBaseClass mybc = (MyBaseClass)derived;        //强制转换成基类
    29             derived.Print();
    30             mybc.Print();
    31             Console.Read();
    32         }
    33     }
    34 }
    35 /*
    36  * 输出如下:
    37  * This is the derived class.
    38  * This is the derived class.
    39  * */

    其他的一些关于virtual和override的信息有:

    □覆写和被覆写的方法必须有相同的可访问性。e.g.不能是 被覆写是private 而覆写的是public

    不能覆写static方法或非虚方法

    □方法、属性、索引器,以及另一种成员类型事件,都可以被声明为virtual和override

    Part2 覆写标记为override的方法:

    覆写方法可以在继承的任何层次中出现

    □当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最高派生(most-derived)版本。

    □如果在更高的派生级别有该方法的其他声明,但没有被标记为override,那么它们不会被调用

    以以下实例来说明,其中三个类构成一个继承的层次:

    MyBaseClass、MyDerivedClass和SecondDerived。其均包含名称为Print的方法,并带有相同的签名。且分别被标记为virtual、override、 override/new 分别看一下第三个类标记为这两个的结果。

     1     class MyBaseClass                           //基类
     2     {
     3         virtual public void Print()
     4         {
     5             Console.WriteLine("This is the base class.");
     6         }
     7     }
     8     class MyDerivedClass : MyBaseClass          //派生类
     9     {
    10         override public void Print()
    11         {
    12             Console.WriteLine("This is the derived class.");
    13         }
    14     }
    15     class SecondDerived : MyDerivedClass        //最高派生类
    16     {
    17         ...// Given in the following pages
    18     }

    1.情况1:使用override声明Print

    如果把SecondDerived的Print方法声明为override,那么它会覆写方法的全部两个低派生级别的版本,如下图所示,如果一个基类的引用被用于调用Print,它会向上传递通过整个链达到类SecondDerived中的实现。

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         virtual public void Print()
    12         {
    13             Console.WriteLine("This is the base class.");
    14         }
    15     }
    16     class MyDerivedClass : MyBaseClass          //派生类
    17     {
    18         override public void Print()
    19         {
    20             Console.WriteLine("This is the derived class.");
    21         }
    22     }
    23     class SecondDerived : MyDerivedClass        //最高派生类
    24     {
    25         public override void Print()
    26         {
    27             Console.WriteLine("This is the second derived class.");
    28         }
    29     }
    30     class Program
    31     {
    32         static void Main(string[] args)
    33         {
    34             SecondDerived derived = new SecondDerived();
    35             MyBaseClass mybc = (MyBaseClass)derived;        //强制转换成基类
    36 
    37             derived.Print();
    38             mybc.Print();
    39             Console.Read();
    40         }
    41     }
    42 }
    43 /*
    44  * 输出如下:
    45  * This is the second derived class.
    46  * This is the second derived class.
    47  * */

    结果是:无论Print是通过派生类调用还是通过基类调用,都会调用最高派生类中的方法。当通过基类调用时,调用被沿着继承层次向上传递。

    2.情况2:使用new声明Print

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         virtual public void Print()
    12         {
    13             Console.WriteLine("This is the base class.");
    14         }
    15     }
    16     class MyDerivedClass : MyBaseClass          //派生类
    17     {
    18         override public void Print()
    19         {
    20             Console.WriteLine("This is the derived class.");
    21         }
    22     }
    23     class SecondDerived : MyDerivedClass        //最高派生类
    24     {
    25         new public void Print()
    26         {
    27             Console.WriteLine("This is the second derived class.");
    28         }
    29     }
    30     class Program
    31     {
    32         static void Main(string[] args)
    33         {
    34             SecondDerived derived = new SecondDerived();
    35             MyBaseClass mybc = (MyBaseClass)derived;        //强制转换成基类
    36 
    37             derived.Print();
    38             mybc.Print();
    39             Console.Read();
    40         }
    41     }
    42 }
    43 /*
    44  * 输出如下:
    45  * This is the second derived class.
    46  * This is the derived class.
    47  * */

    结果是:当方法Print通过SecondDerived的引用调用时,SecondDerived中的方法被执行,正如所期待的那样。然而,当方法通过MyBaseClass的引用调用时,方法调用只向上传递了一级,到达类MyDerived,在那里它被执行。

    本来想使用下列代码进行 virtual -> override -> new ->override 的检验

    1 class ThirdDerived : SecondDerived
    2     {
    3         override public void Print()
    4         {
    5             Console.WriteLine("This is the third derived class.");
    6         }
    7     }

    然而编译器报错为“ThirdDerived.Print()”: 继承成员“SecondDerived.Print()”未标记为 virtual、abstract 或 override,无法进行重写

    不过,当试验virtual-> override->new ->virtual ->override 时却是可以的,而实际上,这个中间多出的virtual是在对之前的一系列方法进行隐藏,即这个virtual其实应该写作 new virtual。

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         virtual public void Print()
    12         {
    13             Console.WriteLine("This is the base class.");
    14         }
    15     }
    16     class MyDerivedClass : MyBaseClass          //派生类
    17     {
    18         override public void Print()
    19         {
    20             Console.WriteLine("This is the derived class.");
    21         }
    22     }
    23     class SecondDerived : MyDerivedClass        //最高派生类
    24     {
    25         new public void Print()
    26         {
    27             Console.WriteLine("This is the second derived class.");
    28         }
    29     }
    30     class ThirdDerived : SecondDerived
    31     {
    32         new virtual public void Print()
    33         {
    34         }
    35     }
    36     class FourthDerived : ThirdDerived
    37     {
    38         override public void Print()
    39         {
    40             Console.WriteLine("This is the fourth derived class.");
    41         }
    42     }
    43     class Program
    44     {
    45         static void Main(string[] args)
    46         {
    47             FourthDerived derived = new FourthDerived();
    48             MyBaseClass mybc = (MyBaseClass)derived;        //强制转换成基类
    49 
    50             derived.Print();
    51             mybc.Print();
    52             Console.Read();
    53         }
    54     }
    55 }
    56 /*
    57  * 输出如下:
    58  * This is the fourth derived class.
    59  * This is the derived class.
    60  * */

    综上,对覆写方法的总结为:

    通过某个引用对方法进行调用时,实际执行的是从这里出发,连续的一段override覆写中的最后一个的方法。(中间不可以用virtual隔开)。

    Part3 覆盖其他成员类型

    以上已经描述了如何在方法上使用virtual/override,事实上,在属性事件以及索引器上也是一样的。e.g.下面的代码演示了名为MyProperty的只读属性,其中使用了virtual/override

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         private int _mInt = 5;
    12         virtual public int MyProperty
    13         {
    14             get { return _mInt; }
    15         }
    16     }
    17     class MyDerivedClass : MyBaseClass          //派生类
    18     {
    19         private int _mInt = 10;
    20         public override int MyProperty
    21         {
    22             get
    23             {
    24                 return _mInt;
    25             }
    26         }
    27     }
    28     class Program
    29     {
    30         static void Main(string[] args)
    31         {
    32             MyDerivedClass derived = new MyDerivedClass();
    33             MyBaseClass mybc = (MyBaseClass)derived;        //强制转换成基类
    34 
    35             Console.WriteLine(derived.MyProperty);
    36             Console.WriteLine(mybc.MyProperty);
    37             Console.Read();
    38         }
    39     }
    40 }
    41 /*
    42  * 输出如下:
    43  * 10
    44  * 10
    45  * */

    这里需要注意的是,最终执行下去返回的属性就是派生类的属性。

    p.s. 使用virtual声明,并不影响方法or属性or事件or索引的执行(字段无法被virtual修饰)。virtual修饰的方法or属性or事件or索引仍可以有函数体。

    如下例所示

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace review
     8 {
     9     class MyBaseClass                           //基类
    10     {
    11         private int _mInt = 5;
    12         virtual public int MyProperty
    13         {
    14             get { return _mInt; }
    15         }
    16     }
    17     class Program
    18     {
    19         static void Main(string[] args)
    20         {
    21             MyBaseClass mybc = new MyBaseClass();
    22             Console.WriteLine(mybc.MyProperty);
    23             Console.Read();
    24         }
    25     }
    26 }
    27 /*
    28  * 输出如下:
    29  * 5
    30  * */
  • 相关阅读:
    php获取某年某月的天数
    处理银行卡每隔4位数用空格隔开(正则表达式)
    刚看到一个前端面试题, 左边固定,右边自适应, 就根据自己想的自己写了下试试
    Yii中利用filters来控制访问
    Yii中使用RBAC完全指南
    自动把 替换成<p></p>
    统计汉字
    php执行linux函数
    java 与 R 相互调用
    Deep Learning 深度学习 学习教程网站集锦(转)
  • 原文地址:https://www.cnblogs.com/quintessence/p/9100751.html
Copyright © 2020-2023  润新知