• C# 第五章『面向对象』◆第9节:抽象类和密封类


            接口的存在可以使C#中存在多重继承,这样使程序结构更加合理。abstract关键字和sealed关键字分别实现了抽象和密封的定义,这两种方法使程序的设计更加严密。

            一、抽象类和抽象方法

            如果一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类,这样的类就是抽象类,在抽象类中声明方法时,如果加上abstract时就是抽象方法。

            1、抽象类

            抽象类与非抽象类的主要区别:

            ①抽象类不能直接被实例化

            ②抽象类中可以包含抽象成员,但非抽象类中不可以。

            ③抽象类不能被密封。

    1. public abstract class myClass
    2. {
    3. //抽象成员
    4. }

            2、抽象方法

            在抽象类中也可以使用关键字abstract定义抽象方法,要求所有派生的非抽象类都要重载实现抽象方法,引入抽象方法的原因在于抽象类本身是一种抽象概念,有的方法并不需要具体实现,而是留下来让派生类重载实现。

            注意点:

            ①抽象方法必须声明在抽象类中。

            ②声明抽象方法时,不能使用virtual、static和private修饰符。

            ③抽象方法声明引入一个新的方法,但是不提供具体的实现。

            ④当从抽象类中派生一个非抽象类时,需要在非抽象类中重写所有抽象类中的方法,提供具体实现细节,重写抽象方法时候使用override关键字。

    1. //抽象方法为:
    2. public abstract int Method();
    3. //派生类重载实现为:
    4. public override int Method()
    5. {
    6. //抽象方法体
    7. }

            备注:抽象方法必须声明在抽象类中,并且不能包含方法体。

    1. using System;
    2. namespace Project16
    3. {
    4. //定义基类Shape
    5. public abstract class Shape //声明一个抽象类的图形
    6. {
    7. //protected的作用:允许派生类访问它的基类成员
    8. protected string Color; //表示图形的颜色
    9. //构造函数的作用帮助用户初始化对象,也就是给对象的每个属性依次赋值
    10. public Shape(string Color) //定义带参的构造函数,为图形的颜色赋值
    11. {
    12. this.Color = Color;
    13. }
    14. public string GetColor() //获取图形颜色的方法GetColor
    15. {
    16. return Color;
    17. }
    18. public abstract double GetArea(); //表示图形的面积
    19. //(1) 抽象方法必须声明在抽象类中。
    20. //(2) 声明抽象方法时,不能使用virtual、static和private修饰符。
    21. //(3) 抽象方法声明引入一个新的方法,但是不提供具体的实现。
    22. }
    23. //定义Cirle类,从Shape类中派生
    24. public class Circle : Shape //圆形
    25. {
    26. //private的作用:只有在同一类中的方法,才能够去访问该变量
    27. private double Radius; //圆的半径
    28. //通过base关键字,指定创建派生类实例时应调用的基类构造函数。
    29. public Circle(string Color, double Radius) : base(Color)
    30. {
    31. this.Color = Color;
    32. this.Radius = Radius;
    33. }
    34. //当从抽象类中派生中一个非抽象类时,需要使用override关键字,来实现非抽象类中的方法
    35. public override double GetArea() //重写抽象方法
    36. {
    37. return System.Math.PI * Radius * Radius;
    38. }
    39. }
    40. //派生类Rectangular,从Shape类中派生
    41. public class Retangular : Shape //矩形
    42. {
    43. protected double Length, Width; //声明长和宽
    44. public Retangular(string Color, double Length, double Width) : base(Color) //构造函数
    45. {
    46. this.Color = Color;
    47. this.Length = Length;
    48. this.Width = Width;
    49. }
    50. public override double GetArea() //重写方法
    51. {
    52. return (Length * Width);
    53. }
    54. public double PerimeterIs() //周长
    55. {
    56. return (2 * (Length + Width));
    57. }
    58. }
    59. //派生类Square,从Retangular类中派生
    60. public class Square : Retangular //正方形
    61. {
    62. public Square(string Color, double Side) : base(Color, Side, Side) {; }
    63. }
    64. class Program
    65. {
    66. static void Main(string[] args)
    67. {
    68. Circle Cir = new Circle("黄色", 4.0);
    69. Console.WriteLine("圆 形 的颜色是:{0},它的面积是:{1}", Cir.GetColor(), Cir.GetArea());
    70. Retangular Rect = new Retangular("红色", 6.0, 8.0);
    71. Console.WriteLine("矩 形 的颜色是:{0},它的面积是:{1},它的周长是:{2}",
    72. Rect.GetColor(), Rect.GetArea(), Rect.PerimeterIs());
    73. Square Squ = new Square("绿色", 5.0);
    74. Console.WriteLine("正方形的颜色是:{0},它的面积是:{1},它的周长是:{2}",
    75. Squ.GetColor(), Squ.GetArea(), Squ.PerimeterIs());
    76. }
    77. }
    78. }

            二、抽象类与接口的区别

            1、抽象类

            含有abstract修饰符的class即为抽象类,抽象类是特殊的类,只是不能被实例化,可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例;除此之外,抽象类具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明与抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖。

            2、接口

            接口是引用类型,类似于类。接口和抽象类的相似之处有三点:

            ①不能实例化

            ②包含为实现的方法声明。

            ③派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)

            接口具有如下特性:

            接口除了可以包含方法之外,还可以包含属性、索引器、事件,而这些成员都被定义为公有的。除此之外,不能包含任何其他成员,例如常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。

            3、两者的区别

            ①抽象类可以有构造方法,接口中不能有构造方法。

            ②抽象类中可以有普通成员变量,接口中没有普通成员变量。

            ③抽象类中的出现方法的访问类型可以是public、protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

            ④抽象类中可以包含静态方法,接口中不能包含静态方法。

            ⑤一个类可以实现多个接口,但只能继承一个抽象类。

            ⑥抽象类实现的具体方法默认为虚方法,但实现接口的类中的接口方法却默认为非虚方法,当然用户也可以声明为虚方法。

            三、密封类和密封方法

            如果所有类都可以被继承,那么很容易导致继承的滥用,进而使类的层次结构体系变得十分复杂,为了避免滥用继承,C#中提出了密封的概念。

            1、密封类

            密封类可以用来限制扩展性,如果密封了某个类,则其他类不能从该类继承;如果密封了某个成员,则派生类不能重写该成员的实现。

            密封类的声明格式为:

    1. 访问修饰符 sealed class 类名:基类或接口
    2. {
    3. 类体
    4. }

            说明:

            ①密封类不能作为基类被继承,但它可以继承别的类或接口。

            ②在密封类中不能声明受保护成员或虚成员,因为受保护成员只能从派生类进行访问,而虚成员只能在派生类中重写。

            ③由于密封类的不可继承性,密封类不能声明为抽象的,即sealed修饰符不能与abstract修饰符同时使用。

    1. class A
    2. {}
    3. sealed class B:A //密封类B
    4. {} //类体
    5. class C:B //类C无法从密封类B派生
    6. {} //类体

            2、密封方法

            并不是每个方法都可以声明为密封方法,密封方法只能用于对基类的虚方法进行实现,并提供具体的实现。所以,声明密封方法时,sealed修饰符总是和override修饰符同时使用。

            密封方法的声明格式为:

    1. 访问修饰符 sealed override 方法名称(参数列表)
    2. {
    3. 方法体
    4. }

            其中,访问修饰符、参数列表都是可选的。

    1. using System;
    2. namespace Project17
    3. {
    4. public class MyClass1
    5. {
    6. public virtual void Write()
    7. {
    8. Console.WriteLine("这是一个未密封的方法");
    9. }
    10. }
    11. public class MyClass2:MyClass1
    12. {//继承之后需要对虚方法Write进行重写
    13. public sealed override void Write()
    14. {
    15. Console.WriteLine("这是一个密封的方法");
    16. }
    17. }
    18. public class MyClass3:MyClass2
    19. {
    20. //public override sealed void Write()
    21. //{
    22. // Console.WriteLine("重写密封方法");
    23. //}
    24. //继承成员“MyClass2.Write()”是密封的,所以无法进行重写
    25. }
    26. class Program
    27. {
    28. static void Main(string[] args)
    29. {
    30. MyClass2 myClass2 = new MyClass2();
    31. myClass2.Write();
    32. }
    33. }
    34. }

            3、密封类域密封方法的适用

            密封类除了不能被继承外,与非密封类的用法大致相同,而密封方法则必须通过重写基类中的虚方法来实现。

    1. using System;
    2. namespace Project18
    3. {
    4. public class MyClass1
    5. {
    6. public virtual void ShowInfo() //虚方法,用来显示信息
    7. {
    8. }
    9. }
    10. public sealed class MyClass2 : MyClass1 //密封类,继承 MyClass1
    11. {
    12. private string _id = "";
    13. private string _name = "";
    14. public string ID
    15. {
    16. get
    17. {
    18. return _id;
    19. }
    20. set
    21. {
    22. _id = value;
    23. }
    24. }
    25. public string Name
    26. {
    27. get
    28. {
    29. return _name;
    30. }
    31. set
    32. {
    33. _name = value;
    34. }
    35. }
    36. public sealed override void ShowInfo()
    37. {
    38. Console.WriteLine("我是{0},我的ID是{1}", Name, ID);
    39. }
    40. }
    41. class Program
    42. {
    43. static void Main(string[] args)
    44. {
    45. MyClass2 myclass2 = new MyClass2();
    46. myclass2.ID = "BH0001";
    47. myclass2.Name = "张三";
    48. myclass2.ShowInfo();
    49. }
    50. }
    51. }

            注意:在声明密封方法时,必须通过重写基类中的虚方法实现。

  • 相关阅读:
    VL53L5CX驱动开发(4)----运动指示器
    区块链溯源相比传统追溯有什么优点?
    JSTL循环Map
    k8s client-go源码分析 informer源码分析(5)-Controller&Processor源码分析
    ShardingSphere-JDBC实战
    Java项目:SSM学生会管理系统
    助力汽修企业突破转型瓶颈,S2B2B商城价格管理模块实现采购交易数字化升级
    mPaas小程序(支付宝、钉钉...)自定义组件,组件传参
    【GAN小白入门】Semi-Supervised GAN 理论与实战
    Vivado与Notepad++关联步骤
  • 原文地址:https://blog.csdn.net/qq_45336030/article/details/126211049