C# 中的封装与抽象
阅读:32
点赞:0
一. 封装的概念
封装是将数据(变量)与操作这些数据的方法(函数)结合成一个统一的实体或类的概念。这个原则还涉及通过将某些元素指定为私有(private)或保护(protected)来限制对其访问。通过隐藏内部实现,封装确保只有必要的信息被公开,从而增强了数据访问和修改的管理。
封装示例
using System;
namespace EncapsulationExample
{
public class UserBankAccountDetails
{
// 封装的私有字段
private string accountNumber; // 账户号码
private decimal initialBalance; // 初始余额
// 构造函数初始化账户信息
public UserBankAccountDetails(string accountNumber, decimal initialBalance)
{
this.accountNumber = accountNumber; // 设置账户号码
this.initialBalance = initialBalance; // 设置初始余额
}
// 公共方法获取账户余额
public decimal GetUserBalance()
{
return initialBalance; // 返回初始余额
}
// 公共方法存款
public void AmountDepositedByUser(decimal amount)
{
if (amount > 0) // 检查存款金额是否为正
initialBalance += amount; // 增加余额
else
throw new ArgumentException("存款金额必须为正。"); // 抛出异常
}
// 公共方法取款
public void GetAmountWithdrawnByUser(decimal amount)
{
if (amount > 0 && amount <= initialBalance) // 检查取款金额是否合法
initialBalance -= amount; // 减少余额
else
throw new ArgumentException("无效的取款金额。"); // 抛出异常
}
}
class Program
{
static void Main()
{
UserBankAccountDetails useraccount = new UserBankAccountDetails("01245431", 2000);
// 通过受控方法访问余额(封装)
useraccount.AmountDepositedByUser(500); // 存款500美元
useraccount.GetAmountWithdrawnByUser(200); // 取款200美元
// 尝试取款超过余额(受控验证)
// useraccount.GetAmountWithdrawnByUser(400); // 无效取款
// 使用受控方法检查余额
Console.WriteLine($"最终余额: {useraccount.GetUserBalance():C}"); // 输出最终余额
}
}
}
封装类的解释
-
私有字段:余额字段是私有的,意味着不能直接从 UserBankAccountDetails
类外部访问。这隐藏了内部状态。 -
公共方法: AmountDepositedByUser
、GetAmountWithdrawnByUser
和GetUserBalance
方法提供了对私有initialBalance
字段的受控访问,确保余额只能通过有效方式进行修改。 -
数据完整性:存款和取款逻辑保持了数据的完整性,防止无效数据(如负存款或超出余额的取款)。
封装的优势
-
增强安全性:敏感信息可以被隐藏,只通过受控方式公开,防止未经授权的代码改变内部状态。 -
简化维护:修改类的内部工作(如余额的计算或存储方式)可以在不影响外部代码的情况下进行,只要公共方法接口保持不变,其他组件无需调整。 -
受控数据修改:通过管理数据的访问和修改,可以强加规则(如不允许无效值),确保对象始终保持有效状态。 -
适应性和可扩展性:封装使内部实现或逻辑的变化无需修改外部合同,从而增强了代码的灵活性,并便于未来的扩展。
二. 抽象的概念
抽象强调隐藏系统的实现细节,只展示必要的功能。在C#中,抽象通过使用抽象类或接口实现,允许用户了解方法或属性,而不暴露其操作的底层代码。
C#中使用接口的抽象示例
using System;
namespace AbstractionExample
{
public interface IVehicleDetails
{
void StartVehicle(); // 启动车辆
void StopVehicle(); // 停止车辆
void DriveVehicle(); // 驾驶车辆
}
public class CarMechanism : IVehicleDetails
{
public void StartVehicle()
{
Console.WriteLine("汽车发动机已启动。"); // 启动汽车引擎
}
public void StopVehicle()
{
Console.WriteLine("汽车已停止。"); // 停止汽车
}
public void DriveVehicle()
{
Console.WriteLine("汽车正在驾驶中。"); // 驾驶汽车
}
}
public class BikeMechanism : IVehicleDetails
{
public void StartVehicle()
{
Console.WriteLine("摩托车发动机已启动。"); // 启动摩托车引擎
}
public void StopVehicle()
{
Console.WriteLine("摩托车已停止。"); // 停止摩托车
}
public void DriveVehicle()
{
Console.WriteLine("摩托车正在驾驶中。"); // 驾驶摩托车
}
}
}
public class Program
{
static void Main(string[] args)
{
IVehicleDetails myVehicle = new CarMechanism();
myVehicle.StartVehicle(); // 输出:汽车发动机已启动。
myVehicle.DriveVehicle(); // 输出:汽车正在驾驶中。
myVehicle.StopVehicle(); // 输出:汽车已停止。
myVehicle = new BikeMechanism();
myVehicle.StartVehicle(); // 输出:摩托车发动机已启动。
myVehicle.DriveVehicle(); // 输出:摩托车正在驾驶中。
myVehicle.StopVehicle(); // 输出:摩托车已停止。
}
}
C#中使用抽象类的抽象示例
抽象类是不能独立实例化的类,旨在供其他类继承。它可以包括抽象方法(子类必须实现)和非抽象方法(已有实现)。
public abstract class VehicleDetails
{
public string Name { get; set; } // 车辆名称
public int MaxSpeed { get; set; } // 最高速度
// 抽象方法 - 必须由派生类实现
public abstract void StartVehicle();
// 抽象方法 - 必须由派生类实现
public abstract void StopVehicle();
// 抽象方法 - 必须由派生类实现
public abstract void DriveVehicle();
// 非抽象方法 - 所有车辆共享
public void DisplayInfo()
{
Console.WriteLine($"车辆: {Name}, 最高速度: {MaxSpeed} km/h"); // 输出车辆信息
}
}
public class CarMechanism : VehicleDetails
{
public override void StartVehicle() // 实现抽象方法
{
Console.WriteLine("汽车发动机已启动。"); // 启动汽车引擎
}
public override void StopVehicle() // 实现抽象方法
{
Console.WriteLine("汽车已停止。"); // 停止汽车
}
public override void DriveVehicle() // 实现抽象方法
{
Console.WriteLine("汽车正在驾驶中。"); // 驾驶汽车
}
}
public class BikeMechanism : VehicleDetails
{
public override void StartVehicle() // 实现抽象方法
{
Console.WriteLine("摩托车发动机已启动。"); // 启动摩托车引擎
}
public override void StopVehicle() // 实现抽象方法
{
Console.WriteLine("摩托车已停止。"); // 停止摩托车
}
public override void DriveVehicle() // 实现抽象方法
{
Console.WriteLine("摩托车正在驾驶中。"); // 驾驶摩托车
}
}
public class Program
{
static void Main(string[] args)
{
// 创建派生类的实例
VehicleDetails myCar = new CarMechanism { Name = "轿车", MaxSpeed = 180 };
VehicleDetails myBike = new BikeMechanism { Name = "山地车", MaxSpeed = 25 };
// 使用抽象来启动、停止和显示信息
myCar.DisplayInfo(); // 输出:车辆: 轿车, 最高速度: 180 km/h
myCar.StartVehicle(); // 输出:汽车发动机已启动。
myCar.StopVehicle(); // 输出:汽车已停止。
myCar.DriveVehicle(); // 输出:汽车正在驾驶中。
myBike.DisplayInfo(); // 输出:车辆: 山地车, 最高速度: 25 km/h
myBike.StartVehicle(); // 输出:摩托车发动机已启动。
myBike.StopVehicle(); // 输出:摩托车已停止。
myBike.DriveVehicle(); // 输出:摩托车正在驾驶中。
}
}
抽象的优势
-
简化代码结构:通过隐藏复杂性,抽象减少了类的复杂性,使其更易于使用和理解。 -
增强可维护性:可以轻松修改抽象类的实现,而不影响依赖于抽象类的客户端代码。 -
可扩展性:新的类可以在不改变现有代码的情况下,继承抽象类并实现其抽象方法,增加新的功能。 -
多态性:抽象允许将不同的具体类视为相同的类型,简化了代码并增强了灵活性。
三. 封装与抽象的比较
-
目标:
-
封装旨在隐藏数据细节和实现。 -
抽象旨在突出功能性,隐藏实现细节。
-
-
实现方式:
-
封装通过访问修饰符和类实现。 -
抽象通过抽象类和接口实现。
-
-
关注点:
-
封装关注数据的安全性和完整性。 -
抽象关注系统功能的可用性和简洁性。
-
四. 结论
通过在C#中有效地运用封装和抽象,可以创建安全且易于维护和扩展的系统。封装通过限制对数据的直接访问,保护了数据的完整性,而抽象则通过隐藏实现细节,提供了对系统功能的更高层次的视图。这两者结合使用,将极大提高代码的质量与可维护性,是现代软件开发中不可或缺的原则。