C#(五):接口、泛型、struct、继承、方法的隐藏与重写、密封、抽象、多态

妖狐艹你老母 2024-03-30 12:03 128阅读 0赞

接口、泛型、struct、继承、方法的隐藏与重写、密封、抽象、多态

    • 接口
      • 用途
      • 特点
      • 语法
      • 代码
    • 泛型
      • 使用在类中的泛型-特点:
      • 使用在方法中的泛型-特点:
      • 使用在接口中的泛型-特点:
    • struct
      • 结构体是 `值类型` `struct`
      • 结构体与类的区别
    • 继承
      • 示例
      • 继承之构造方法
      • 向上转型(隐式类型转换) - 子类转换成父类
      • 向下转型(需要强制类型转换) - 父类转换成子类
    • 方法的隐藏与重写
      • 方法隐藏
      • 方法重写
    • 密封
      • 密封类
      • 密封方法
    • 抽象
      • `抽象类`
      • `抽象方法`
    • 多态 `virtual` `abstract` `interface`

接口

用途

  • 定义一系列的规范

特点

  • 接口中方法不是抽象方法(接口中方法的实现不能使用 override)
  • 接口中方法的实现必须是 public 权限的
  • 抽象类实现接口方法可以把接口方法实现为抽象方法
  • 一个类中实现接口方法可以把方法实现为虚方法

语法

  • 一个类可以实现多个接口
  • 一个类继承另一个类并且实现接口需要把接口下载继承类的后边,并用逗号分隔
  • 区分接口与继承规范:接口命名以大写字母I开头

代码

  1. interface IUSB
  2. {
  3. // 充电
  4. public void Charge();
  5. // 数据传输
  6. void TransportData();
  7. }
  8. class Hardware {
  9. }
  10. // 让Mouse去实现USB这个接口
  11. class Mouse : Hardware, IUSB
  12. {
  13. // 一个类中实现接口方法可以把方法实现为虚方法
  14. public virtual void Charge() {
  15. }
  16. public void TransportData() {
  17. }
  18. }
  19. // 抽象类实现接口方法可以把接口方法实现为抽象方法
  20. abstract class Keyword : IUSB
  21. {
  22. public abstract void Charge();
  23. public void TransportData() {
  24. }
  25. }

泛型

使用在类中的泛型-特点:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 使用一个泛型
  6. Person<int> person = new Person<int>();
  7. person.Show(23);
  8. // 使用多个泛型
  9. Animal<string, uint, char> animal = new Animal<string, uint, char>
  10. {
  11. name = "Dog",
  12. age = 23,
  13. sex = '男'
  14. };
  15. Console.WriteLine(animal.name);
  16. Console.WriteLine(animal.age);
  17. Console.WriteLine(animal.sex);
  18. }
  19. }
  20. class Person<T> {
  21. public T Show(T x)
  22. {
  23. Console.WriteLine(x);
  24. return x;
  25. }
  26. }
  27. class Animal<T, M, D>
  28. {
  29. public T name;
  30. public M age;
  31. public D sex;
  32. }
  33. class Animal<T, M>
  34. {
  35. public T name;
  36. public M age;
  37. }
  38. class Animal
  39. {
  40. public string name;
  41. public uint age;
  42. }
  43. class Dog : Animal
  44. {
  45. }
  46. class Lee : Person<string>
  47. {
  48. }

使用在方法中的泛型-特点:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Person.Show<string>();
  6. Person.Show<string>("Hello World ~");
  7. Person.Show("Hello World ~");
  8. Console.ReadKey();
  9. }
  10. }
  11. class Person {
  12. public static void Show<T>()
  13. {
  14. Console.WriteLine("Hello World");
  15. }
  16. public static void Show<T>(T x)
  17. {
  18. Console.WriteLine(x);
  19. }
  20. }

使用在接口中的泛型-特点:

  1. interface IUSB<T> {
  2. void Show(T x);
  3. }
  4. class Mouse : IUSB<string> {
  5. public void Show(string x)
  6. {
  7. Console.WriteLine(x);
  8. }
  9. }

struct

结构体是 值类型 struct

  1. struct Person
  2. {
  3. // 静态构造
  4. static Person(){
  5. }
  6. // 有参构造
  7. public Person(string name, int age, char sex, double height) {
  8. this.name = name;
  9. this.age = age;
  10. this.sex = sex;
  11. this.height = height;
  12. }
  13. //字段
  14. public string name;
  15. public int age;
  16. public char sex;
  17. public double height;
  18. public static double weight; // 静态字段
  19. //属性
  20. public double Height
  21. {
  22. set {
  23. this.height = value;
  24. }
  25. get {
  26. return height;
  27. }
  28. }
  29. //方法
  30. public void FunA() {
  31. Console.WriteLine("Person Show ~");
  32. }
  33. };
  34. class Program
  35. {
  36. static void Main(string[] args)
  37. {
  38. Person lee = new Person
  39. {
  40. age = 23
  41. };
  42. Change(lee);
  43. Console.WriteLine(lee.age); // 230
  44. Console.ReadKey();
  45. }
  46. public static void Change(Person person) {
  47. person.age = 230;
  48. }
  49. }

结构体与类的区别

  • 相同点

    • 都可以包含字段属性和方法
    • 都可以通过new来实例化
    • 都可以配置访问权限修饰符
  • 不同点

    • 结构体是在栈上开辟的空间,是值类型,不是引用类型

      1. class Person
      2. {
      3. public int age;
      4. };
      5. class Program
      6. {
      7. static void Main(string[] args)
      8. {
  1. Person lee = new Person();
  2. lee.age = 23;
  3. Change(lee);
  4. Console.WriteLine(lee.age); // 230
  5. Console.ReadKey();
  6. }
  7. public static void Change(Person person) {
  8. person.age = 230;
  9. }
  10. }
  11. struct Person
  12. {
  13. public int age;
  14. };
  15. class Program
  16. {
  17. static void Main(string[] args)
  18. {
  19. Person lee = new Person();
  20. lee.age = 23;
  21. Change(lee);
  22. Console.WriteLine(lee.age); // 23
  23. Console.ReadKey();
  24. }
  25. public static void Change(Person person) {
  26. person.age = 230;
  27. }
  28. }
  29. * 构造方法
  30. * 结构体中不允许写无参构造
  31. * 结构体的有参构造方法中必须为所有的字段进行赋值操作
  32. struct Person
  33. {
  34. public Person(string name, int age, char sex, double height) {
  35. this.name = name; // 必须赋值
  36. this.age = age; // 必须赋值
  37. this.sex = sex; // 必须赋值
  38. this.height = height; // 必须赋值
  39. }
  40. //字段
  41. public string name;
  42. public int age;
  43. public char sex;
  44. public double height;
  45. }
  46. * 结构体中始终包含一个public无参的构造方法,所以 `Person p = new Person();` 不报错
  47. * 结构体中不允许写析构函数
  48. * 结构体中只有一个父类 `Object` ,不能被继承也不能继承

继承

  1. 如果多个类中拥有共同的属性和方法,那么可以提取出公共的属性和方法定义为一个新类,此类即为 父类(基类/超类)
  2. 多个具有相同属性和方法的类被称为 子类(派生类)
  3. 他们之间的关系是: 子类 继承 父类
  4. 特点:
    a. 子类拥有父类的所有字段、属性和方法。
    b. 构造方法不能被继承。
    c. 一个类只能有一个父类,可以拥有多个子类。

示例

定义一个硬件类,包括 CPU 硬盘 内存 显卡 主板 电源 类,他们拥有共同属性为 名称 价格 属性

  1. class MainClass
  2. {
  3. public static void Main(string[] args)
  4. {
  5. CPU cpu = new CPU
  6. {
  7. Name = "CPU"
  8. };
  9. cpu.SetPrice(100d);
  10. Console.WriteLine(cpu.Name); // CPU
  11. Console.WriteLine(cpu.GetPrice()); // 100
  12. GPU gpu = new GPU
  13. {
  14. Name = "GPU"
  15. };
  16. gpu.SetPrice(120d);
  17. Console.WriteLine(gpu.Name); // GPU
  18. Console.WriteLine(gpu.GetPrice()); // 120
  19. }
  20. }
  21. /// <summary>
  22. /// 硬件类 - 父类
  23. /// </summary>
  24. class Hardware
  25. {
  26. public string Name;
  27. private double Price;
  28. public void SetPrice(double price)
  29. {
  30. Price = price;
  31. }
  32. public double GetPrice()
  33. {
  34. return Price;
  35. }
  36. }
  37. /// <summary>
  38. /// CPU 继承 硬件
  39. /// </summary>
  40. class CPU : Hardware {
  41. }
  42. /// <summary>
  43. /// 显卡 继承 硬件
  44. /// </summary>
  45. class GPU : Hardware {
  46. }
  47. /// <summary>
  48. /// 硬盘 继承 硬件
  49. /// </summary>
  50. class Disk : Hardware {
  51. }
  52. /// <summary>
  53. /// 内存 继承 硬件
  54. /// </summary>
  55. class Memory : Hardware {
  56. }
  57. /// <summary>
  58. /// 主板 继承 硬件
  59. /// </summary>
  60. class MainBoard : Hardware {
  61. }
  62. /// <summary>
  63. /// 电源 继承 硬件
  64. /// </summary>
  65. class PowerSupply : Hardware {
  66. }

继承之构造方法

  • 报错代码

    1. Dog dog = new Dog(); // Error 子类在实例化的时候会先实例化父类中的无参构造方法
    2. class Animal {
  1. public string name;
  2. public Animal(string name)
  3. {
  4. this.name = name;
  5. }
  6. }
  7. class Dog : Animal {
  8. // Error
  9. public uint age;
  10. }
  • 报错原因:

    • 原因一:子类在实例化的时候会先实例化父类中的无参构造方法,因为Animal中无无参构造方法

      • 解决(方案一):父类提供一个无参构造方法

        1. Dog dog = new Dog();
        2. class Animal {
  1. public string name;
  2. public Animal()
  3. {
  4. }
  5. public Animal(string name)
  6. {
  7. this.name = name;
  8. }
  9. }
  10. class Dog : Animal {
  11. public uint age;
  12. }
  13. * 原因二:`子类在实例化的时候会先实例化父类中的构造方法,因为Animal中是有参的构造方法,所以需要在子类构造之前先调用父类中的有参构造方法`
  14. * 解决(方案二):`类构造之前先调用父类中的有参构造方法`
  15. Dog dog = new Dog();
  16. class Animal {
  17. public string name;
  18. public Animal(string name)
  19. {
  20. this.name = name;
  21. }
  22. }
  23. class Dog : Animal {
  24. public uint age;
  25. // base(参数) -> 执行Dog构造方法之前先去执行他的父类构造方法
  26. public Dog() : base("")
  27. {
  28. }
  29. }
  30. * 解决思想:`想办法让父类实例化的时候合理化(让父类实例化的时候不会报错)`

向上转型(隐式类型转换) - 子类转换成父类

  • 向上转型一定成功

    1. Dog dog = new Dog();
    2. Animal animal = dog;
    3. // => Animal animalDog = new Dog();
    4. // => sbyte b = 1; int a = b; => int ab = 1;
    5. class Animal {
    6. }
    7. class Dog : Animal {
    8. }
    9. class Hashiqi : Dog {
    10. }
  • 示例:动物园动物录入

    1. class MainClass
    2. {
    3. public static void Main(string[] args)
    4. {
    5. Animal.RecordAnimal(new Bird()); // 录入动物
    6. // => 详单于
    7. // Animal animal = new Bird(); // 向上转型
    8. // Animal.RecordAnimal(animal);
    9. }
    10. }
    11. class Animal {
    12. public static void RecordAnimal(Animal animal) {
    13. }
    14. }
    15. class Bird : Animal {
    16. }
    17. class Monkey : Animal {
    18. }
    19. class Tiger : Animal {
    20. }
  • 向上转型后的对象将不再能够使用子类中特有的字段属性和方法

    1. class MainClass
    2. {
    3. public static void Main(string[] args)
    4. {
    5. Hashiqi hashiqi = new Hashiqi();
    6. hashiqi.type = ""; // ok
    7. hashiqi.sex = ' '; // ok
    8. hashiqi.name = ""; // ok
    9. hashiqi.age = 1; // ok
  1. Dog dog = new Dog();
  2. //dog.type = ""; // Error “Dog”未包含“type”的定义,并且找不到可接受第一个“Dog”类型参数的可访问扩展方法“type”
  3. dog.sex = ' '; // ok
  4. dog.name = ""; // ok
  5. dog.age = 1; // ok
  6. Animal animal = new Animal();
  7. //animal.type = ""; // Error “Animal”未包含“type”的定义,并且找不到可接受第一个“Animal”类型参数的可访问扩展方法“type”
  8. //animal.sex = ' '; // Error “Animal”未包含“sex”的定义,并且找不到可接受第一个“Animal”类型参数的可访问扩展方法“sex”
  9. animal.name = ""; // ok
  10. animal.age = 1; // ok
  11. Animal d = new Hashiqi();
  12. //d.type = ""; // Error “Animal”未包含“type”的定义,并且找不到可接受第一个“Animal”类型参数的可访问扩展方法“type”
  13. //d.sex = ' '; // Error “Animal”未包含“sex”的定义,并且找不到可接受第一个“Animal”类型参数的可访问扩展方法“sex”
  14. d.name = ""; // ok
  15. d.age = 1; // ok
  16. Dog dd = new Hashiqi();
  17. //dd.type = ""; // Error “Dog”未包含“type”的定义,并且找不到可接受第一个“Dog”类型参数的可访问扩展方法“type”
  18. dd.sex = ' '; // ok
  19. dd.name = ""; // ok
  20. dd.age = 1; // ok
  21. }
  22. }
  23. class Animal {
  24. public string name;
  25. public int age;
  26. }
  27. class Dog : Animal {
  28. public char sex;
  29. }
  30. class Hashiqi : Dog {
  31. public string type;
  32. }

向下转型(需要强制类型转换) - 父类转换成子类

  • 如果向下转型失败,那么得到 null

    1. // Dog dog = (Dog)new Animal(); // 不推荐
    2. Dog dog = new Animal() as Dog;
    3. Console.WriteLine(dog == null); // True
    4. class Animal {
    5. }
    6. class Dog : Animal {
    7. }
    8. class Hashiqi : Dog {
    9. }
  • 转型成功

    1. class MainClass
    2. {
    3. public static void Main(string[] args)
    4. {
    5. Animal animal = new Dog(); // 向上转型
    6. Dog dog = animal as Dog; // 向下转型
    7. Console.WriteLine(dog); // Dog
    8. Console.WriteLine(dog == null); // False
    9. }
    10. }
    11. class Animal {
    12. }
    13. class Dog : Animal {
    14. }
    15. class Hashiqi : Dog {
    16. }
  • 向下转型错误示例

    1. class MainClass
    2. {
    3. public static void Main(string[] args)
    4. {
    5. Hashiqi hashiqi = new Animal() as Hashiqi; // 向下转型
    6. Console.WriteLine(hashiqi == null); // True
    7. Console.WriteLine(hashiqi.type == null); // Error System.NullReferenceException
    8. }
    9. }
    10. class Animal {
    11. public string name;
    12. public int age;
    13. }
    14. class Dog : Animal {
    15. public char sex;
    16. }
    17. class Hashiqi : Dog {
    18. public string type;
    19. }

方法的隐藏与重写

方法隐藏

  • 父类中含有的字段属性和方法,在子类中也需要定义类似的字段属性和方法,那么就需要隐藏继承自父类的字段属性和方法

    1. class MainClass
    2. {
    3. public static void Main(string[] args)
    4. {
    5. Animal a = new Dog();
    6. a.Show(); // Animal.Show() -> Animal Show
    7. Dog d = new Dog();
    8. d.Show(); // Dog.Show() -> Dog Show
    9. }
    10. }
    11. class Animal {
    12. public void Show() {
    13. Console.WriteLine("Animal Show");
    14. }
    15. }
    16. class Dog : Animal {
  1. // public void Show()
  2. // 父类中含有name属性,子类还需要定义name属性,需要隐藏继承父类的属性,需要使用new
  3. public new void Show()
  4. {
  5. Console.WriteLine("Dog Show");
  6. }
  7. }

方法重写

  • 需要使用关键字 override虚函数(virtual)

    • 虚函数 -> 能被子类隐藏和重写
    • 非虚函数 -> 能被子类隐藏,不能被重写
    • 重写函数

      1. class MainClass
      2. {
      3. public static void Main(string[] args)
      4. {
      5. Animal a = new Dog();
      6. a.Show(); // Animal.Show() -> Dog Show 因为被重写了
      7. Dog d = new Dog();
      8. d.Show(); // Dog.Show() -> Dog Show
      9. }
      10. }
      11. class Animal {
  1. // 子类中的方法重写父类中的方法,需要将父类中的方法设置为虚函数 virtual
  2. public virtual void Show() {
  3. Console.WriteLine("Animal Show");
  4. }
  5. }
  6. class Dog : Animal {
  7. // 子类中的方法重写父类中的方法 override
  8. public override void Show()
  9. {
  10. Console.WriteLine("Dog Show");
  11. }
  12. }
  13. * 隐藏函数
  14. class MainClass
  15. {
  16. public static void Main(string[] args)
  17. {
  18. Animal a = new Dog();
  19. a.Show(); // Animal.Show() -> Animal Show
  20. Dog d = new Dog();
  21. d.Show(); // Dog.Show() -> Dog Show
  22. }
  23. }
  24. class Animal {
  25. // 子类中的方法重写父类中的方法,需要将父类中的方法设置为虚函数 virtual
  26. public virtual void Show() {
  27. Console.WriteLine("Animal Show");
  28. }
  29. }
  30. class Dog : Animal {
  31. // 隐藏父类方法
  32. public new void Show()
  33. {
  34. Console.WriteLine("Dog Show");
  35. }
  36. }

密封

密封类

  • 特点:不能被继承

    1. sealed class Person {
    2. }
    3. class Lee : Person {
    4. } // Error “Lee”: 无法从密封类型“Person”派生

密封方法

  • 特点:不能被继续重写,并且只有重写方法才能使用密封

    1. class Animal {
    2. public virtual void Show() {
    3. }
    4. }
    5. class Dog : Animal {
    6. public override sealed void Show() {
    7. }
    8. }
    9. class Hashiqi : Dog{
    10. public override void Show() {
    11. } // Error “Hashiqi.Show()”: 继承成员“Dog.Show()”是密封的,无法进行重写
    12. }
    13. class Animal {
    14. public virtual sealed void Show() {
    15. } // Error 因为“Animal.Show()”不是重写,所以无法将其密封
    16. }

抽象

  • 定义一种规范,用于约束子类的行为

抽象类

  • 不能实例化对象
  • 抽象类中可以写静态和非静态的成员
  • 抽象类可以被继承和继承其他类

抽象方法

  • 抽象方法只能存在在抽象类中

    • 一个类继承自抽象类,那么这个类中必须实现父类中的所有抽象方法

      1. // 抽象类
      2. abstract class Animal {
      3. // 抽象方法
      4. public abstract void Eat();
      5. }
      6. class Dog : Animal {
      7. // 通过重写实现父类中的所有抽象方法
      8. public override void Eat() {
      9. }
      10. }

多态 virtual abstract interface

实现多态主要有3种方法: 虚方法抽象类接口

发表评论

表情:
评论列表 (有 0 条评论,128人围观)

还没有评论,来说两句吧...

相关阅读