C#系列-面向对象特性继承封装多态(2)

  1. C# 继承

在C#中,继承是面向对象编程的一个核心概念,它允许一个类(派生类或子类)继承另一个类(基类或父类)的属性和方法。通过继承,子类可以重用基类的代码,同时还可以添加新的成员或重写基类的成员。

以下是一个简单的C#继承示例:

csharp 代码

|---|------------------------------------------|
| | // 基类(父类) |
| | public class Animal |
| | { |
| | public void Eat() |
| | { |
| | Console.WriteLine("The animal eats."); |
| | } |
| | |
| | public void Sleep() |
| | { |
| | Console.WriteLine("The animal sleeps."); |
| | } |
| | } |
| | |
| | // 派生类(子类) |
| | public class Dog : Animal |
| | { |
| | // Dog类继承了Animal类的所有公共和保护成员 |
| | // 因此,Dog类可以直接使用Eat和Sleep方法 |
| | |
| | // Dog类可以添加新的成员 |
| | public void Bark() |
| | { |
| | Console.WriteLine("The dog barks."); |
| | } |
| | } |
| | |
| | class Program |
| | { |
| | static void Main(string[] args) |
| | { |
| | // 创建Dog类的实例 |
| | Dog myDog = new Dog(); |
| | |
| | // 调用继承自Animal类的方法 |
| | myDog.Eat(); // 输出: The animal eats. |
| | myDog.Sleep(); // 输出: The animal sleeps. |
| | |
| | // 调用Dog类特有的方法 |
| | myDog.Bark(); // 输出: The dog barks. |
| | } |
| | } |

在这个例子中,Dog 类继承自 Animal 类。这意味着 Dog 类拥有 Animal 类中定义的所有公共和保护成员(在这个例子中是 Eat 和 Sleep 方法)。此外,Dog 类还可以定义它自己的新成员(在这个例子中是 Bark 方法)。

C#还支持以下类型的继承:

  • 公有继承( Public Inheritance :这是最常见的继承类型,在公有继承中,基类的公有和保护成员在派生类中保持原有的访问级别。
  • 保护继承( Protected Inheritance :在保护继承中,基类的公有和保护成员在派生类中成为保护成员。
  • 私有继承( Private Inheritance :在私有继承中,基类的公有和保护成员在派生类中成为私有成员。这种继承类型在C#中并不直接支持,但可以通过其他方式模拟。

在C#中,默认的继承方式是公有继承。另外,请注意,C#不支持多重继承,即一个类不能直接继承多个基类。但是,C#支持接口的多重实现,这可以作为一种实现类似多重继承效果的方式。

继承有助于组织代码,促进代码重用,并允许你创建更特定和复杂的对象层次结构。然而,过度使用继承可能导致代码变得难以理解和维护,因此在使用继承时需要谨慎考虑。

2.C# 封装

在面向对象编程中,封装(Encapsulation)是一种将对象的属性和行为(即数据和方法)结合在一个单独的实体(即对象)中的过程。封装的主要目的是隐藏对象的内部状态,只通过对象提供的方法来访问和修改这些状态。这样可以确保对象的完整性和安全性,同时使得代码更加模块化和可维护。

在C#中,封装通常是通过定义类来实现的。类定义了一个数据类型,包含数据成员(字段或属性)和方法成员(方法或事件)。类的用户(客户端代码)可以通过对象实例来访问类的公共接口,但不能直接访问对象的内部数据或实现细节。

以下是一个简单的C#封装示例:

csharp 代码

|---|--------------------------------------------------------------------------------------------|
| | // 定义一个简单的类,实现封装 |
| | public class BankAccount |
| | { |
| | // 私有字段,表示账户余额 |
| | private decimal balance; |
| | |
| | // 公共属性,提供对余额的访问和修改 |
| | public decimal Balance |
| | { |
| | get { return balance; } |
| | set |
| | { |
| | // 可以在这里添加逻辑来验证值的有效性 |
| | if (value >= 0) |
| | { |
| | balance = value; |
| | } |
| | else |
| | { |
| | throw new ArgumentException("Balance cannot be negative."); |
| | } |
| | } |
| | } |
| | |
| | // 公共方法,表示存款 |
| | public void Deposit(decimal amount) |
| | { |
| | if (amount > 0) |
| | { |
| | balance += amount; |
| | } |
| | else |
| | { |
| | throw new ArgumentException("Deposit amount must be positive."); |
| | } |
| | } |
| | |
| | // 公共方法,表示取款 |
| | public void Withdraw(decimal amount) |
| | { |
| | if (amount > 0 && balance >= amount) |
| | { |
| | balance -= amount; |
| | } |
| | else |
| | { |
| | throw new InvalidOperationException("Insufficient balance or invalid withdrawal amount."); |
| | } |
| | } |
| | } |
| | |
| | class Program |
| | { |
| | static void Main(string[] args) |
| | { |
| | // 创建BankAccount类的实例 |
| | BankAccount account = new BankAccount(); |
| | |
| | // 使用公共接口进行交互 |
| | account.Balance = 1000; // 设置初始余额 |
| | account.Deposit(500); // 存款 |
| | account.Withdraw(200); // 取款 |
| | |
| | // 无法直接访问私有字段balance |
| | // decimal balanceValue = account.balance; // 这将导致编译错误 |
| | |
| | // 但可以通过公共属性访问余额 |
| | Console.WriteLine("Current balance: " + account.Balance); |
| | } |
| | } |

在这个例子中,BankAccount 类封装了账户余额(balance)这个私有字段,并提供了一个公共属性(Balance)来安全地访问和修改它。此外,还提供了Deposit和Withdraw方法来修改余额。客户端代码(Main方法)通过创建BankAccount类的实例并使用其公共接口来与账户进行交互,而不需要了解账户余额是如何内部存储或管理的。

封装有助于保护对象的内部状态免受外部代码的意外修改,并使得类的实现细节更加安全和可维护。通过将数据和操作封装在一起,可以创建出更加健壮和易于使用的对象。

3.C# 多态

在面向对象编程中,多态(Polymorphism)是一种允许你使用父类类型的引用或接口来引用子类对象的能力。当引用被调用时,将执行与引用所绑定对象的实际类型相对应的方法。这意味着,即使引用是父类类型,也可以调用子类特有的方法。多态允许我们编写更加通用和可重用的代码,因为我们可以编写操作父类类型引用的代码,而无需关心实际对象的具体类型。

在C#中,多态通常通过以下方式实现:

  1. 方法重写(Method Overriding):子类可以重写父类中的虚方法(virtual)、抽象方法(abstract)或覆盖方法(override)。当子类对象被当作父类类型引用时,调用该方法将会执行子类中的重写版本。
  2. 方法隐藏(Method Hiding):子类可以隐藏父类中具有相同名称的方法。这不同于重写,因为隐藏方法不会覆盖父类方法,而是创建了一个新的方法。
  3. 方法重载(Method Overloading):在同一个类中,可以定义多个具有相同名称但参数列表不同的方法。编译器根据调用时提供的参数来确定要调用哪个方法。
  4. 接口实现(Interface Implementation):类可以实现一个或多个接口,并提供接口中定义的方法的具体实现。这使得可以通过接口引用来调用实现类中的方法。

下面是一个简单的C#多态示例:

csharp 代码

|---|---------------------------------------------|
| | // 定义一个动物基类 |
| | public class Animal |
| | { |
| | public virtual void Speak() |
| | { |
| | Console.WriteLine("The animal speaks."); |
| | } |
| | } |
| | |
| | // 定义一个狗类,继承自动物基类并重写Speak方法 |
| | public class Dog : Animal |
| | { |
| | public override void Speak() |
| | { |
| | Console.WriteLine("The dog barks."); |
| | } |
| | } |
| | |
| | // 定义一个猫类,继承自动物基类并重写Speak方法 |
| | public class Cat : Animal |
| | { |
| | public override void Speak() |
| | { |
| | Console.WriteLine("The cat meows."); |
| | } |
| | } |
| | |
| | class Program |
| | { |
| | static void Main(string[] args) |
| | { |
| | // 创建一个Animal类型的数组,用于存储不同类型的动物对象 |
| | Animal[] animals = new Animal[2]; |
| | |
| | // 创建Dog和Cat对象,并赋值给数组 |
| | animals[0] = new Dog(); |
| | animals[1] = new Cat(); |
| | |
| | // 遍历数组,调用每个动物的Speak方法 |
| | foreach (var animal in animals) |
| | { |
| | animal.Speak(); // 这里将调用相应对象类型的Speak方法,实现多态 |
| | } |
| | } |
| | } |

在这个例子中,Animal 类定义了一个虚方法 Speak,而 Dog 和 Cat 类重写了这个方法。在 Main 方法中,我们创建了一个 Animal 类型的数组,并存储了 Dog 和 Cat 的对象。当我们遍历这个数组并调用每个对象的 Speak 方法时,会根据实际对象的类型(Dog 或 Cat)来执行相应的方法,这就是多态的体现。

相关推荐
500了1 小时前
Kotlin基本知识
android·开发语言·kotlin
不知所云,3 小时前
qt cmake自定义资源目录,手动加载资源(图片, qss文件)
开发语言·qt
安冬的码畜日常3 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
阑梦清川3 小时前
Java继承、final/protected说明、super/this辨析
java·开发语言
PythonFun4 小时前
Python批量下载PPT模块并实现自动解压
开发语言·python·powerpoint
Death2004 小时前
Qt 6 相比 Qt 5 的主要提升与更新
开发语言·c++·qt·交互·数据可视化
机器视觉知识推荐、就业指导4 小时前
使用Qt实现实时数据动态绘制的折线图示例
开发语言·qt
快乐就好ya5 小时前
Java多线程
java·开发语言
CS_GaoMing5 小时前
Centos7 JDK 多版本管理与 Maven 构建问题和注意!
java·开发语言·maven·centos7·java多版本
2401_858120536 小时前
Spring Boot框架下的大学生就业招聘平台
java·开发语言