C# 中的封装与抽象

发布:2024-10-22 11:28 阅读: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类外部访问。这隐藏了内部状态。
  • 公共方法AmountDepositedByUserGetAmountWithdrawnByUserGetUserBalance方法提供了对私有initialBalance字段的受控访问,确保余额只能通过有效方式进行修改。
  • 数据完整性:存款和取款逻辑保持了数据的完整性,防止无效数据(如负存款或超出余额的取款)。

封装的优势

  1. 增强安全性:敏感信息可以被隐藏,只通过受控方式公开,防止未经授权的代码改变内部状态。
  2. 简化维护:修改类的内部工作(如余额的计算或存储方式)可以在不影响外部代码的情况下进行,只要公共方法接口保持不变,其他组件无需调整。
  3. 受控数据修改:通过管理数据的访问和修改,可以强加规则(如不允许无效值),确保对象始终保持有效状态。
  4. 适应性和可扩展性:封装使内部实现或逻辑的变化无需修改外部合同,从而增强了代码的灵活性,并便于未来的扩展。

二. 抽象的概念

抽象强调隐藏系统的实现细节,只展示必要的功能。在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 { getset; } // 车辆名称
    public int MaxSpeed { getset; } // 最高速度

    // 抽象方法 - 必须由派生类实现
    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();    // 输出:摩托车正在驾驶中。
    }
}

 

抽象的优势

  1. 简化代码结构:通过隐藏复杂性,抽象减少了类的复杂性,使其更易于使用和理解。
  2. 增强可维护性:可以轻松修改抽象类的实现,而不影响依赖于抽象类的客户端代码。
  3. 可扩展性:新的类可以在不改变现有代码的情况下,继承抽象类并实现其抽象方法,增加新的功能。
  4. 多态性:抽象允许将不同的具体类视为相同的类型,简化了代码并增强了灵活性。

三. 封装与抽象的比较

  1. 目标

    • 封装旨在隐藏数据细节和实现。
    • 抽象旨在突出功能性,隐藏实现细节。
  2. 实现方式

    • 封装通过访问修饰符和类实现。
    • 抽象通过抽象类和接口实现。
  3. 关注点

    • 封装关注数据的安全性和完整性。
    • 抽象关注系统功能的可用性和简洁性。

四. 结论

通过在C#中有效地运用封装和抽象,可以创建安全且易于维护和扩展的系统。封装通过限制对数据的直接访问,保护了数据的完整性,而抽象则通过隐藏实现细节,提供了对系统功能的更高层次的视图。这两者结合使用,将极大提高代码的质量与可维护性,是现代软件开发中不可或缺的原则。