• .net---继承和多态


    继承和多态的基本概念

    继承

    • 允许无需重新改写现有类,重用现有类(基类,亦称超类、父类)去创建新类(子类,亦称派生类

    • C#所有类继承于Object类
      在这里插入图片描述

    • 多态性
      对象可以表示多个类型的能力,以自己的方式响应同一消息

    继承的类型

    实现继承

    • 一个类型派生于一个基类型,派生类具有基类的所有非私有(非private)数据和行为

    • 派生类型的每个方法采用基类型的实现代码,除非在派生类型的定义中指定重写该方法的实现代码

    • 一般用于增加现有类型的功能,或许多相关的类型共享一组重要的公共功能的场合

    接口继承

    • 一个类型实现若干接口,接口仅包含方法的签名,不继承任何实现代码

    • 一般用于指定该类型具有某类可用的特性,在接口中定义通用的实现代码是没有意义的

    • 接口即契约,类型派生于接口,即保证该类提供该接口规定的功能

    继承

    派生类

    [类修饰符] class 类名 [: 基类] {类体;}

    • 在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)
    • C#的一个派生类只能继承于一个基类
    class Point    //等同于:class Point : Object
    {
        int x, y;
    }
    class ColoredPoint : Point  //派生类,从Point派生
    {
        int color;
    }
    class Colored3dPoint : ColoredPoint//派生类,从ColoredPoint派生
    {
        int z;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    base关键字

    用于从派生类中访问基类的成员
    base(参数):指定创建派生类实例时应调用的基类构造函数
    base.方法(参数):调用基类上已被其他方法重写的方法
    base.字段名:访问基类的数据成员

    不能从静态方法中使用 base 关键字
    只能在实例构造函数、实例方法或实例访问器中使用

    public class Person 
    //等同于:class Person : Object
    { 
        public String name;  public int age;
        public Person(String name, int age) 
    //基类构造函数
        { 
            this.name = name;  this.age = age;
        }
        public void print()  //基类的实例方法
        {             
            Console.Write("name={0},age={1}", this.name, this.age);
        }
    }
    public class Student : Person //派生类
    {          
        public String studentID;
        public Student(String name, int age, String id)
            : base(name, age) 
    //派生类构造函数,使用base调用基类构造函数
        { 
            this.studentID = id;
        }
        public new void print()
        {
            base.print();          
    //使用base调用基类的方法
            Console.WriteLine(",studentID={0}", this.studentID);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    构造函数的调用

    构造函数不能被继承,也不能被覆盖,名称必须与类名相同
    通过关键字new创建对象实例时,会根据传入的参数调用相匹配的构造函数
    调用构造函数前,需要先调用其基类的构造函数

    类成员的继承

    通过继承,派生类继承基类中除构造函数之外的所有成员
    与基类成员的访问修饰符无关

    类成员的隐藏

    如果在派生类中声明了与继承的成员同名的成员,则该重名成员将隐藏从基类继承的同名成员变量

    class Parent  // 基类Parent
    {
        public String name;
        public String sex;
    }
    class Child : Parent      // 派生类Child
    {
        public String name;  
    // 成员变量,隐藏从基类继承的同名成员变量name
        public Child(String name, String sex) // 构造函数
        {
            this.name = name;            
    //this.name引用Child声明的成员变量
            this.sex = sex;               
    //this.sex引用从Parent继承的成员变量
            base.name = name + "'s parent"; 
    //base.name引用从基类Parent继承的同名成员变量name
        }
        public void print()
        {
            Console.WriteLine(this.name + " " + this.sex + " " + base.name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    继承:虚方法和隐藏方法

    • 虚方法
      在基类中,使用virtual关键字声明的方法

    • 隐藏方法
      在派生类中,使用new关键字声明的与基类中相同签名的方法
      派生类中相同签名方法重新定义了基类中的方法,即隐藏了基类中的同名方法

    • 重写方法
      在派生类中,使用关键字override声明的与基类中相同签名的方法

    在这里插入图片描述

    • 基类的虚方法声明用于引入新方法
    • 派生类的重写方法用于使继承的虚方法专用化
    • 虚方法和重写方法主要用于实现多态

    抽象类和抽象方法

    抽象类

    • 将关键字 abstract 置于关键字 class 的前面可以将类声明为抽象类

    • 抽象类不能实例化(不能用new),一般用于提供多个派生类可共享的基类的公共定义

    • 可以定义抽象类型的变量,但其值必须为 null,或者是其派生的非抽象类的实例的引用

    • 允许(但不要求)抽象类包含抽象成员

    • 抽象类不能被密封

    • 当从抽象类派生非抽象类时,这些非抽象类必须实现所继承的所有抽象成员,从而重写那些抽象成员

    抽象方法

    抽象类中通过将关键字 abstract 添加到实例方法的返回类型的前面可以定义抽象方法

    • 只允许在抽象类中声明抽象方法
    • 抽象方法同时隐含为虚方法,但不能有virtual修饰符
    • 抽象类的派生类如果是非抽象类,则必须重写抽象基类的所有抽象方法
    • 派生类中,不能用base直接引用抽象基类的抽象方法
     abstract class Animal      
    // 基类Animal:抽象类
        {
            public String name;
            public abstract void SayHi();
         public Animal(String name) 
         { this.name = name; }  //构造函数
        }
    
        class Dog : Animal        // 派生类Dog
        {    //重写SayHi()
    public override void SayHi() { Console.WriteLine(this.name + " Wow Wow!"); }
    public Dog(String name) : base(name) { }   
    //构造函数
        }
    
        class Cat : Animal        // 派生类Cat
        {  //重写SayHi()
            public override void SayHi() { Console.WriteLine(this.name + " Mew Mew!"); }
            public Cat(String name) : base(name) { }            //构造函数
        }
    
        //class Horse : Animal { } //编译错误,非抽象类Horse继承了抽象类Animal,但未实现抽象方法
        abstract class Fish : Animal //编译OK,抽象类Fish继承了抽象类Animal,但未实现抽象方法
        {
            public Fish(String name) : base(name) { }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    密封类和密封方法

    密封类

    通过将关键字 sealed 置于关键字 class 的前面,可以将类声明为密封类

    • 不能用作基类,也不能是抽象类
    • 主要用于防止非有意的派生
    • 如果实例方法声明包含sealed 修饰符,则它必须也包含 override 修饰符
    • 把类或方法声明为抽象,表示该类或方法必须被重写或继承;而把类或方法声明为密封,表示该类或方法不能重写或继承

    接口

    接口定义协定

    在这里插入图片描述

    • 接口类似于抽象基类,不能实例化
    • 接口只指定实现该接口的类或结构必须提供的成员,不提供实现
    • 继承接口的任何非抽象类型必须实现接口的所有成员
    • 接口可以包含事件、索引器、方法和属性,但不能包含字段

    接口成员

    • 接口成员只能包含方法、属性、索引器和事件的声明
    • 所有接口成员都隐式地具有public访问属性,不能包含任何修饰符

    接口实现

    接口可由类和结构实现

    [类修饰符] class 类名:基接口列表
    {
         类体;
    }
    
    • 1
    • 2
    • 3
    • 4

    接口分部与继承

    public interface IDimensions  //基类:接口IDimensions
    {
        float getLength(); float getWidth();
    }
      public class Box : IDimensions    //派生类Box
      {
        float length;  float width;
        Box(float length, float width)
        {
            this.length = length;  this.width = width;
        }
        public float getLength() { return length;}
        public float getWidth() { return width; }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    多态

    • 派生类对象可以表示多个类型的能力

    • 类方法多态性的实现有两种方式:方法重载和方法重写

    在这里插入图片描述

    重载

    class P0 // 基类
    { 
        public void MethodA(){Console.WriteLine("调用类P0的方法methodA()");}
    }
    class C0 : P0 // 派生类
    { 
        public void MethodA(String str1) //重载方法:重载从基类继承的MethodA()
        { 
          Console.WriteLine("调用类C0的方法MethodA():" + str1);
        }
        public void MethodB(String str1) //重载方法:重载MethodB(int a)
        { 
          Console.WriteLine("调用C0类的实例方法MethodB(String str1):" + str1);
        }
        public void MethodB(int a) // 重载方法:重载MethodB(String str1)
        { 
          Console.WriteLine("调用C0类的实例方法MethodB(int a):" + a);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    重写

    class A0 // 基类
    { 
        public virtual void MethodA()
        {
            Console.WriteLine("调用A0的类的实例方法MethodA()");
        }
    }
    class A01 : A0 // 派生类
    { 
        public override void MethodA()//重写方法:重写从基类A0继承的方法
        {    
            Console.WriteLine("调用A01的类的实例方法MethodA()");
        }
    }
    class A02 : A0 // 派生类
    {
        public override void MethodA()//重写方法:重写从基类A0继承的方法
        {    
            Console.WriteLine("调用A02的类的实例方法MethodA()");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    Tomcat为什么支持线程池?
    【CentOS7】安装docker
    Unity性能优化分析篇
    微信小程序项目源码ssm校园跑腿+后台管理系统|前后分离VUE含论文+PPT+源码
    SQl 经验总结
    在AgilePLM项目中使用积木报表
    Mysql---第七篇
    C++ 类型转换
    【华为OD机试真题 JS】数组拼接
    esaypoi导入excel工具模版
  • 原文地址:https://blog.csdn.net/weixin_51422230/article/details/128023812