• C# 第五章『面向对象』◆第6节:类的继承


            一、基类和派生类

            在程序设计中实现继承,表示这个类拥有它继承的类的所有公有成员或受保护成员。在面向对象程序设计中,被继承的类称为父类基类(原有的类),实现继承的类成为子类派生类(新生的类)。

            当一个类从另一个类派生出来时,派生类就自然具有基类的数据成员、属性和方法等。在基类中定义的这些成员,已经不需要在派生类定义中重写。在派生类的定义中,只需编写基类所不具备的代码即可。

    1. <访问修饰符>class<父类的名称>
    2. {
    3. A
    4. B
    5. }
    6. class<派生类>:<父类的名称>
    7. {
    8. A
    9. B
    10. C
    11. }

            在C#中适用“:”来标识两个类的继承关系。在继承一个类时,类成员的可访问性是一个重要的问题。派生类(子类)不能访问基类(父类)的私有成员,但是可以访问其公共成员,这就是说,只要使用public声明类成员,就可以让一个类成员被基类(父类)和派生类(子类)同时访问,同时也可以被外部的代码访问。

            另外,为了解决基类(父类)成员的访问问题,C#还提供了另一种可访问性:protected,它表示受保护成员,只有基类(父类)和派生类(子类)才能访问protected成员,外部代码不能访问protected成员。

            备注:

            ①派生类不能继承基类中定义的private成员。

            ②C#中只支持单继承,而不支持多重继承,即在C#中一次只允许继承一个类,不能同时继承多个类。

            ③在实现类的继承中,子类的可访问性一定要低于或等于父类的可访问性。

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. namespace _20220805_1
    6. {
    7. class Goods
    8. {
    9. public string TradeCode { get; set; }
    10. public string FullName { get; set; }
    11. }
    12. class JHInfo : Goods
    13. {
    14. public string JHID { get; set; }
    15. public void showInfo()
    16. {
    17. Console.WriteLine("进货编号:{0}\n商品编号:{1}\n商品名称:{2}",JHID,TradeCode,FullName);
    18. }
    19. }
    20. class Program
    21. {
    22. static void Main(string[] args)
    23. {
    24. JHInfo jh = new JHInfo();
    25. jh.TradeCode = "T101";
    26. jh.FullName = "笔记本";
    27. jh.JHID = "JH001";
    28. jh.showInfo();
    29. Console.ReadLine();
    30. }
    31. }
    32. }

            二、继承的特性

            继承是在类之间建立一种相交的关系,使得新定义的派生类的实例,可以继承已有的基类的特征,并且可以添加新的功能。

            特征:

    1. 派生类只能继承一个基类。所以C#并不支持多重继承,但一个基类可以有多个直接派生类。
    2. 子类应当是对父类的扩展。子类可以添加新的成员,但是不能去去除已经继承的成员的定义。
    3. 继承是可以传递的。例如:c类从b类中派生,而b类又从a类冲派生,那么c类不仅继承了b类的所有成员,同样也继承了a类中的成员。Object作为所有类的父类,我们称之为祖先类。
    4. 构造函数和析构函数不能被继承(需通过base)。除此以外的其他成员,不论对它们定义了怎样的访问方式,都能被继承。父类成员的访问方式只能决定子类是否能访问它们。
    5. 覆盖)子类如果定义了与继承而来的成员同名的新成员,则可以覆盖已继承的成员。但并不意味着删除了继承的成员,只是不能再直接访问这些成员。
    6. 如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
    7. 类可以定义虚方法、虚属性以及虚索引指示器,它的子类能够重载这些成员,从而实现类的多态性。
    1. //派生类只能继承一个基类,所以C#并不支持多重继承,但一个基类可以有多个直接派生类。
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. namespace ConsoleApplication3
    7. {
    8. class Clerk
    9. {
    10. private string _name;
    11. public string Name //定义职员的姓名属性
    12. {
    13. get { return _name; }
    14. set { _name = value; }
    15. }
    16. private string _department; //定义职员部分的字段
    17. public string Department //定义职员部门的属性
    18. {
    19. get { return _department; }
    20. set { _department = value; }
    21. }
    22. public void CSeyHello()
    23. {
    24. Console.WriteLine("大家好,我是{0}的{1}", Department, Name);
    25. }
    26. }
    27. class Sales:Clerk //创建销售类Sales,继承与Clerk
    28. {
    29. private int _salesTarget; //定义职员销售目标的字段
    30. public int SalesTarget //定义职员销售目标的属性
    31. {
    32. get { return _salesTarget; }
    33. set { _salesTarget = value; }
    34. }
    35. public void SSayHello()
    36. {
    37. Console.WriteLine("大家好,我是{0}的{1},我的销售目标是{2}元", Department, Name,SalesTarget);
    38. }
    39. }
    40. //创建技术支持类TechnicalSupport,继承于Clerk
    41. class TechnicalSupport:Clerk
    42. {
    43. private double _satisfactionRate;
    44. public double SatisfactionRate
    45. {
    46. get { return _satisfactionRate; }
    47. set { _satisfactionRate = value; }
    48. }
    49. public void TSSayHello()
    50. {
    51. Console.WriteLine("大家好,我是{0}的{1},我的客户打分是{2}分", Department, Name,SatisfactionRate);
    52. }
    53. }
    54. class Program
    55. {
    56. static void Main(string[] args)
    57. {
    58. Clerk zs = new Clerk();
    59. zs.Name = "张三";
    60. zs.Department = "事业部";
    61. zs.CSeyHello();
    62. //--------------------------------
    63. Sales ls = new Sales();
    64. ls.Name = "李四";
    65. ls.Department = "销售部";
    66. ls.SalesTarget = 800000;
    67. ls.SSayHello();
    68. //--------------------------------
    69. TechnicalSupport zh = new TechnicalSupport();
    70. zh.Name = "王五";
    71. zh.Department = "售后部";
    72. zh.SatisfactionRate = 99;
    73. zh.TSSayHello();
    74. }
    75. }
    76. }
    1. //如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
    2. using System;
    3. namespace Project3
    4. {
    5. class myClass1
    6. {
    7. public void Write()
    8. {
    9. Console.WriteLine("这里是基类");
    10. }
    11. }
    12. class myClass2 : myClass1
    13. {
    14. public new void Write() //隐藏方法
    15. {
    16. Console.WriteLine("这里是派生类");
    17. }
    18. }
    19. class Program
    20. {
    21. static void Main(string[] args)
    22. {
    23. myClass2 b = new myClass2();
    24. myClass1 a = b;
    25. a.Write();
    26. b.Write();
    27. }
    28. }
    29. }

            1问:子类继承了父类,那么子类从父类那里继承了什么?

            1答:子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。

            2问:子类有没有继承父类的构造函数

            2答:子类并没有继承父类的构造函数,但是子类会默认的调用父类无参数的构造函数,创建父类对象,让子类可以使用父类中的成员。所以,如果在父类中重新写一个有参数的构造函数后,那么那个无参数的构造函数就会被自动去除。子类就调用不到了,所以子类会报错。

            解决方案:在父类中重新写一个无参的构造函数;②在子类中显示的调用父类的构造函数,适用关键字base()。

            三、禁止继承

            有些时候,我们不希望自己编写的类被继承,或者不需要将自己开发的类被派生。在C#语言中,将不能够被继承的类或者不可以作为父类的类,称为封闭类。

            声明封闭类需要在class前面加sealed修饰符,被声明为sealed的类不能够作为父类。一般主要在以下两种情况下适用封闭类。

    1. 当一个类不可能再有子类从这个类派生时,需要将这个类声明为封闭的类。
    2. 如果一个类中所有的方法和属性都被声明为静态(static),则需要将这个类声明为封闭的类。

            四、base关键字

            当子类重写了父类的方法,就无法调用父类的方法了。如果需要在子类的方法中实现原有父类的方法,C#提供了base关键字。

            base关键字用于从派生类中访问基类的成员。base关键字的使用方法与this关键字类似,this关键字代表本类对象,base关键字代表父类对象。

            备注:如果要在子类中适用base关键字调用父类的属性或方法,则父类的属性和方法必须定义为public类型或protected类型。

                       访问父类成员只能在构造函数、实例方法或实例属性中进行,因此,在静态方法中适用base关键字是错误的。

    1. base.property;//调用父类的属性
    2. base.method();//调用父类的方法

            指定创建派生类实例时应调用基类构造函数,用于调用基类的构造函数完成对基类成员的初始化工作。

            在使用base时,要注意base指的是调用“对象”本身,不仅是指基类中看到的变量或方法。

            注意:

            ①通过base不仅可以访问直接基类中定义的域和方法,还可以访问间接基类中定义的域和方法。

            ②在构造方法中调用基类的构造方法时,base()指直接基类的构造方法,而不能指间接基类的构造方法,这是因为构造方法是不能继承的。

            ③由于base指的是对象,所以它不能在static环境中使用,包括静态域、静态方法和static构造方法。

    1. using System;
    2. namespace Demo
    3. {
    4. class Computer //父类:电脑
    5. {
    6. public string sayHello()
    7. {
    8. return "欢迎使用";
    9. }
    10. }
    11. class Pad : Computer //子类:平板电脑
    12. {
    13. public new string sayHello() //子类重写父类方法
    14. {
    15. return base.sayHello() + "平板电脑"; //调用父类方法,在结果后添加字符串
    16. }
    17. }
    18. class Program
    19. {
    20. static void Main(string[] args)
    21. {
    22. Computer pc = new Computer(); //电脑类
    23. Console.WriteLine(pc.sayHello());
    24. Pad ipad = new Pad(); //平板电脑类
    25. Console.WriteLine(ipad.sayHello());
    26. Console.ReadLine();
    27. }
    28. }
    29. }

            五、继承中的构造函数与析构函数

            在进行类的继承时,派生类的构造函数会隐式调用基类的无参构造函数,但是如果基类也是从其他类派生的,则c#会根据层次结构找到顶层的基类,并调用基类的构造函数,然后在依次调用各级派生类的构造函数。析构函数的执行顺序正好与构造函数相反。

            ①当基类中没有定义构造函数时,派生类会默认地调用基类的默认构造函数。

            ②当基类中编写一个有参构造函数时,再实例化派生类的对象,程序就会调用基类中无参的构造函数,如果该函数存在就调用它,否则编译器会报错。因此,不论程序调用派生类中的哪个构造函数,都是在寻找基类中无参的构造函数,如果没有则报错,而非参数匹配。

            ③基类中编写了构造函数,可以通过base关键字,指定创建派生类实例时应调用基类构造函数。

    1. using System;
    2. namespace Project4
    3. {
    4. public class myClass1 //基类
    5. {
    6. int Number;
    7. public myClass1() //构造函数
    8. {
    9. Console.WriteLine("派生类调用基类的第一个构造函数");
    10. }
    11. public myClass1(int a)
    12. {
    13. Number = a;
    14. Console.WriteLine("派生类调用基类的第二个构造函数");
    15. }
    16. public int GetNumber()
    17. {
    18. return Number;
    19. }
    20. }
    21. public class myClass2 : myClass1 //派生类
    22. {
    23. //这个构造函数调用第一个基类的构造函数
    24. public myClass2() : base() //派生类的构造函数
    25. {
    26. }
    27. //这个构造函数调用第二个基类的构造函数
    28. public myClass2(int a) : base(a) //派生类的第二个构造函数
    29. {
    30. }
    31. static void Main()
    32. {
    33. myClass2 bs1 = new myClass2();
    34. myClass2 bs2 = new myClass2(1);
    35. }
    36. }
    37. }
  • 相关阅读:
    最新 | VDA-ISA5.0.4最新版本发布,汽车企业如何增强信息安全?
    同态加密为什么能被称为密码学的“圣杯”?
    在CentOS 7上关闭SELinux
    基于opengauss数据库的酒水销售管理系统
    springboot集成kafka
    CDR和AI哪个软件更好用?
    【ICASSP 2023】ST-MVDNET++论文阅读分析与总结
    端口扫描-安全体系-网络安全技术和协议
    计算机毕业设计Java银创科技有限公司人事信息系统(系统+程序+mysql数据库+Lw文档)
    GBase 8d的分布式特性-Referral特性
  • 原文地址:https://blog.csdn.net/qq_45336030/article/details/126185717