C# 继承、多态性、抽象和接口详解:从入门到精通

C# 继承

C# 中,可以将字段和方法从一个类继承到另一个类。我们将"继承概念"分为两类:

  • 派生类(子类) - 从另一个类继承的类
  • 基类(父类) - 被继承的类

要从一个类继承,使用 : 符号。

在以下示例中,Car 类(子类)继承了 Vehicle 类(父类)的字段和方法:

示例

csharp 复制代码
class Vehicle  // 基类(父类)
{
  public string brand = "Ford";  // 车辆字段
  public void honk()             // 车辆方法
  {
    Console.WriteLine("Tuut, tuut!");
  }
}

class Car : Vehicle  // 派生类(子类)
{
  public string modelName = "Mustang";  // 汽车字段
}

class Program
{
  static void Main(string[] args)
  {
    // 创建一个 myCar 对象
    Car myCar = new Car();

    // 在 myCar 对象上调用 honk() 方法(来自 Vehicle 类)
    myCar.honk();

    // 显示 brand 字段(来自 Vehicle 类)的值和 modelName 字段(来自 Car 类)的值
    Console.WriteLine(myCar.brand + " " + myCar.modelName);
  }
}

输出

复制代码
Tuut, tuut!
Ford Mustang

为什么以及何时使用"继承"?

  • 它对于代码重用非常有用:在创建新类时重用现有类的字段和方法

sealed 关键字

如果您不希望其他类从一个类继承,请使用 sealed 关键字:

csharp 复制代码
sealed class Vehicle 
{
  ...
}

class Car : Vehicle 
{
  ...
}

如果您尝试访问一个 sealed 类,C# 会生成一个错误:

csharp 复制代码
'Car': cannot derive from sealed type 'Vehicle'

多态性和方法覆盖

多态性意味着 "多种形态",它发生在我们有许多通过继承相互关联的类时。继承允许我们从另一个类继承字段和方法。多态性使用这些方法来执行不同的任务。这允许我们以不同的方式执行单个动作。

例如,考虑一个名为 Animal 的基类,它有一个名为 animalSound() 的方法。Animal 的派生类可以是 PigsCatsDogsBirds,它们也有自己的 animalSound() 方法实现(猪会叫,猫会喵喵叫等)。

示例:

csharp 复制代码
class Animal  // 基类(父类)
{
  public virtual void animalSound() 
  {
    Console.WriteLine("动物发出声音");
  }
}

class Pig : Animal  // 派生类(子类)
{
  public override void animalSound() 
  {
    Console.WriteLine("猪说:wee wee");
  }
}

class Dog : Animal  // 派生类(子类)
{
  public override void animalSound() 
  {
    Console.WriteLine("狗说:bow wow");
  }
}

现在我们可以创建 PigDog 对象,并在它们两个上调用 animalSound() 方法:

示例:

csharp 复制代码
class Program 
{
  static void Main(string[] args) 
  {
    Animal myAnimal = new Animal();  // 创建一个 Animal 对象
    Animal myPig = new Pig();  // 创建一个 Pig 对象
    Animal myDog = new Dog();  // 创建一个 Dog 对象

    myAnimal.animalSound();
    myPig.animalSound();
    myDog.animalSound();
  }
}

输出将为:

复制代码
动物发出声音
猪说:wee wee
狗说

C# 抽象

抽象类和方法

数据抽象是隐藏某些细节并仅向用户显示基本信息的过程。

抽象可以通过抽象类或接口来实现。

abstract 关键字用于类和方法:

  • 抽象类:是一个受限制的类,不能用于创建对象(要访问它,必须从另一个类继承)。
  • 抽象方法:只能在抽象类中使用,并且没有方法体。方法体由派生类(继承自)提供。

抽象类可以同时包含抽象方法和常规方法:

csharp 复制代码
abstract class Animal {
  public abstract void animalSound();
  public void sleep() {
    Console.WriteLine("Zzz");
  }
}

从上面的例子可以看出,无法创建 Animal 类的对象:

csharp 复制代码
Animal myObj = new Animal(); // 将生成错误(无法创建抽象类或接口"Animal"的实例)

要访问抽象类,必须从另一个类继承它

例子

csharp 复制代码
// 抽象类
abstract class Animal {
  // 抽象方法(没有方法体)
  public abstract void animalSound();
  // 常规方法
  public void sleep() {
    Console.WriteLine("Zzz");
  }
}

// 派生类(继承自 Animal)
class Pig : Animal {
  public override void animalSound() {
    // animalSound() 的方法体在这里提供
    Console.WriteLine("The pig says: wee wee");
  }
}

class Program {
  static void Main(string[] args) {
    Pig myPig = new Pig(); // 创建一个 Pig 对象
    myPig.animalSound(); // 调用抽象方法
    myPig.sleep(); // 调用常规方法
  }
}

为什么以及何时使用抽象类和方法?

  • 为了实现安全性------隐藏某些细节,只显示对象的重要细节。
  • 注意:抽象也可以通过接口实现

C# Interface

接口是在 C# 中实现抽象的另一种方式。

接口是一个完全"抽象类",它只能包含抽象方法和属性(没有实际的方法体):

csharp 复制代码
// 接口
interface Animal 
{
  void animalSound(); // 接口方法(没有方法体)
  void run(); // 接口方法(没有方法体)
}

通常,以字母 "I" 开头是一种良好的实践,因为这样可以更容易地记住它是一个接口而不是一个类。

默认情况下,接口的成员是抽象和公共的。

注意:接口可以包含属性和方法,但不能包含字段。

要访问接口方法,接口必须由另一个类"实现"(有点像继承)。要实现接口,请使用冒号符号(与继承一样)。接口方法的实际方法体由"实现"类提供。请注意,在实现接口时,不必使用 override 关键字:

csharp 复制代码
// 接口
interface IAnimal 
{
  void animalSound(); // 接口方法(没有方法体)
}

// Pig "实现"了 IAnimal 接口
class Pig : IAnimal 
{
  public void animalSound() 
  {
    // animalSound() 的方法体在这里提供
    Console.WriteLine("猪说:呜呜");
  }
}

class Program 
{
  static void Main(string[] args) 
  {
    Pig myPig = new Pig();  // 创建一个 Pig 对象
    myPig.animalSound();
  }
} 

接口的注意事项:

  • 与抽象类一样,接口不能用于创建对象(在上面的示例中,在 Program 类中不能创建"IAnimal"对象)。
  • 接口方法没有方法体 - 方法体由"实现"类提供。
  • 在实现接口时,必须覆盖其所有方法。
  • 接口可以包含属性和方法,但不能包含字段/变量。
  • 接口成员默认是抽象和公共的。
  • 接口不能包含构造函数(因为它不能用于创建对象)。

为什么以及何时使用接口?

  1. 为了实现安全性 - 隐藏对象的某些细节,仅显示重要的细节(接口)。
  2. C# 不支持"多继承"(一个类只能继承一个基类)。但是,可以通过接口实现多继承,因为类可以实现多个接口。注意:要实现多个接口,请使用逗号分隔它们(见下面的示例)。

为什么以及何时使用抽象类和方法?

  • 为了实现安全性------隐藏某些细节,只显示对象的重要细节。
  • 注意:抽象也可以通过接口实现

C# 接口

接口是在 C# 中实现抽象的另一种方式。

接口是一个完全"抽象类",它只能包含抽象方法和属性(没有实际的方法体):

csharp 复制代码
// 接口
interface Animal 
{
  void animalSound(); // 接口方法(没有方法体)
  void run(); // 接口方法(没有方法体)
}

通常,以字母 "I" 开头是一种良好的实践,因为这样可以更容易地记住它是一个接口而不是一个类。

默认情况下,接口的成员是抽象和公共的。

注意:接口可以包含属性和方法,但不能包含字段。

要访问接口方法,接口必须由另一个类"实现"(有点像继承)。要实现接口,请使用冒号符号(与继承一样)。接口方法的实际方法体由"实现"类提供。请注意,在实现接口时,不必使用 override 关键字:

csharp 复制代码
// 接口
interface IAnimal 
{
  void animalSound(); // 接口方法(没有方法体)
}

// Pig "实现"了 IAnimal 接口
class Pig : IAnimal 
{
  public void animalSound() 
  {
    // animalSound() 的方法体在这里提供
    Console.WriteLine("猪说:呜呜");
  }
}

class Program 
{
  static void Main(string[] args) 
  {
    Pig myPig = new Pig();  // 创建一个 Pig 对象
    myPig.animalSound();
  }
} 

接口的注意事项:

  • 与抽象类一样,接口不能用于创建对象(在上面的示例中,在 Program 类中不能创建IAnimal对象)。
  • 接口方法没有方法体 - 方法体由"实现"类提供。
  • 在实现接口时,必须覆盖其所有方法。
  • 接口可以包含属性和方法。

C# 多接口

要实现多个接口,请使用逗号分隔它们:

csharp 复制代码
interface IFirstInterface 
{
  void myMethod(); // 接口方法
}

interface ISecondInterface 
{
  void myOtherMethod(); // 接口方法
}

// 实现多个接口
class DemoClass : IFirstInterface, ISecondInterface 
{
  public void myMethod() 
  {
    Console.WriteLine("一些文本..");
  }
  public void myOtherMethod() 
  {
    Console.WriteLine("一些其他文本...");
  }
}

class Program 
{
  static void Main(string[] args) 
  {
    DemoClass myObj = new DemoClass();
    myObj.myMethod();
    myObj.myOtherMethod();
  }
}

最后

为了方便其他设备和平台的小伙伴观看往期文章:

微信公众号搜索:Let us Coding,关注后即可获取最新文章推送

看完如果觉得有帮助,欢迎 点赞、收藏、关注

相关推荐
Achou.Wang22 分钟前
Concurrency patterns - Go 并发模式
开发语言·后端·golang
存在morning22 分钟前
【GO语言开发实践】三 GO 工程化快速上手
开发语言·后端·golang
kyriewen23 分钟前
用户打开飞行模式都能打开你的网站?Service Worker 做离线缓存,PWA 实战
前端·javascript·面试
摇滚侠1 小时前
SpringBoot 面试题 真正的 offer 偏方 Java 基础 Java 高级
java·spring boot·后端
掘金者阿豪1 小时前
跨平台迁移踩坑记:从路径大小写到国产操作系统的那些事
后端
huaiixinsi1 小时前
Java 后端面试高频题整理(02)
java·开发语言·spring·面试·职场和发展·架构·maven
十贝1 小时前
Tailscale 自建 DERP 中继服务器故障排查与修复记录
后端
Nontee1 小时前
Java 后端面试题目全集
java·开发语言·面试
用户6757049885022 小时前
Redis有1亿个Key,如何优雅地找出特定前缀的那10万条?
后端
用户6757049885022 小时前
程序员常犯的坑:别再用 VARCHAR 存 IP 了!用对方式,性能何止提升10倍!
后端