一、什么是封装及面向对象基础概念回顾
在面向对象编程(OOP)中,有三个重要的特性:封装、继承和多态。简单来说,继承允许一个类(子类)继承另一个类(父类)的属性和方法;多态则让不同的对象对同一消息做出不同的响应。而封装,就像是把数据和操作数据的方法捆绑在一起,形成一个独立的单元,并且隐藏内部的实现细节,只对外提供必要的接口。
举个生活中的例子,汽车就是一个封装的实例。我们开车时,只需要知道如何操作方向盘、油门、刹车这些外部接口就可以了,并不需要了解汽车发动机内部复杂的工作原理。同样,在编程中,封装可以提高代码的安全性、可维护性和复用性。
二、封装的核心实现手段
1. 访问修饰符的魔法
访问修饰符用于控制类、字段、方法等的访问权限,就像不同等级的门禁卡一样,决定了谁可以访问这些代码元素。下面是C#中常见的访问修饰符及其具体解释和使用场景:
访问修饰符 | 访问级别 | 具体解释 | 使用场景 |
---|---|---|---|
public | 公共的 | 任何代码都可以访问,就像公共通道,所有人都能自由进出。 | 当你希望某个类、方法或字段可以被项目中的任何地方使用时,比如定义一个公共的工具类方法。 |
private | 私有的 | 只有在定义它的类内部才能访问,如同私人密室,外人无法进入。 | 通常用于隐藏类的内部实现细节,防止外部代码直接访问和修改。比如类中的一些中间计算变量。 |
protected | 受保护的 | 可以在定义它的类以及该类的子类中访问,类似家族通道,只有家族成员(子类)才能使用。 | 当你希望子类能够访问父类的某些成员,但又不希望外部类访问时使用。 |
internal | 内部的 | 可以在同一程序集(简单理解为同一个项目)内的代码中访问,就像公司内部的办公区域,只有公司员工(同一项目的代码)才能进入。 | 用于在项目内部共享代码,而不希望被其他项目直接访问。 |
protected internal | 受保护的内部 | 可以在同一程序集内的任何代码以及该类的子类中访问,不管子类是否在同一程序集。相当于既可以让公司员工使用,也允许家族成员(子类)在其他公司也能使用。 | 当你希望在项目内部以及子类中都能访问某个成员时使用。 |
2. 属性的优雅封装
在C#中,属性是封装字段的标准方式。直接使用公共字段存在一些问题,比如无法对数据进行验证和控制。例如,如果一个类有一个公共的年龄字段,外部代码可以随意将其设置为一个不合理的值(如负数),这可能会导致程序出现错误。而属性通过get和set访问器,可以在读写数据时执行额外的逻辑,从而保证数据的有效性。
// 定义一个Person类
public class Person
{
// 定义一个私有字段,用于存储姓名
// 私有字段只能在类内部访问,外部无法直接修改,保证了数据的安全性
private string _name;
// 定义一个公共属性,用于对外提供对姓名的访问和设置
public string Name
{
// get访问器,用于获取姓名的值
// 当外部代码调用Person对象的Name属性时,会执行这个get访问器
get => _name;
// set访问器,用于设置姓名的值
// 当外部代码给Person对象的Name属性赋值时,会执行这个set访问器
set
{
// 验证输入的姓名是否为空
if (string.IsNullOrEmpty(value))
// 如果为空,抛出一个ArgumentException异常,提示姓名不能为空
throw new ArgumentException("姓名不能为空");
// 验证输入的姓名长度是否超过50
if (value.Length > 50)
// 如果超过50,抛出一个ArgumentException异常,提示姓名过长
throw new ArgumentException("姓名过长");
// 将输入的姓名转换为大写并赋值给私有字段
_name = value.ToUpper();
}
}
}
3. 方法的职责划分
方法的职责划分遵循单一职责原则,即一个方法只负责一项明确的任务。这样可以提高代码的可读性和可维护性。
// 定义订单项类
public class OrderItem
{
// 定义公共属性,用于存储订单项的产品名称
// 每个订单项都有一个对应的产品名称,方便识别订单项的内容
public string ProductName { get; set; }
// 定义公共属性,用于存储订单项的价格
// 价格是订单项的重要信息,用于计算订单总价
public decimal Price { get; set; }
// 定义公共属性,用于存储订单项的数量
// 数量表示该产品在订单中的购买份数,与价格一起计算该订单项的总价
public int Quantity { get; set; }
}
// 定义订单类
public class Order
{
// 定义一个私有字段,用于存储订单项的列表
// 订单由多个订单项组成,使用列表来存储这些订单项
private List<OrderItem> _items = new List<OrderItem>();
// 定义一个公共方法,用于向订单中添加订单项
public void AddItem(OrderItem item)
{
// 将订单项添加到列表中
_items.Add(item);
}
// 定义一个公共方法,用于计算订单的总价
public decimal CalculateTotal()
{
// 遍历订单项列表,计算每个订单项的总价并求和
return _items.Sum(i => i.Price * i.Quantity);
}
}
三、封装的高阶应用场景
1. 数据验证层
在实际开发中,我们经常需要对用户输入的数据进行验证,确保数据的有效性。通过封装数据验证逻辑,可以提高代码的复用性和可维护性。
// 定义一个User类
public class User
{
// 定义一个私有字段,用于存储用户的邮箱
private string _email;
// 定义一个公共属性,用于对外提供对邮箱的访问和设置
public string Email
{
// get访问器,用于获取邮箱的值
get => _email;
// set访问器,用于设置邮箱的值
set
{
// 验证输入的邮箱是否有效
if (!IsValidEmail(value))
// 如果无效,抛出一个FormatException异常,提示邮箱格式错误
throw new FormatException("无效邮箱格式");
// 将输入的邮箱赋值给私有字段
_email = value;
}
}
// 定义一个私有方法,用于验证邮箱格式是否有效
private bool IsValidEmail(string email)
{
// 这里可以实现复杂的邮箱验证逻辑,例如使用正则表达式
// 简单示例,判断是否包含@符号
return email.Contains("@");
}
}
2. 懒加载优化
懒加载(延迟初始化)是一种优化技术,它允许我们在真正需要使用某个对象时才进行初始化,而不是在对象创建时就进行初始化。这样可以提高程序的性能,特别是在处理一些占用资源较大的对象时。
// 定义一个Product类
public class Product
{
// 定义一个私有字段,用于存储产品的评论列表
// 初始值为null,表示还没有加载评论
private List<Review> _reviews;
// 定义一个公共属性,用于对外提供对评论列表的访问
public List<Review> Reviews
{
get
{
// 如果评论列表为空,则从数据库加载评论
if (_reviews == null)
{
// 调用私有方法LoadReviewsFromDB从数据库加载评论
_reviews = LoadReviewsFromDB();
}
// 返回评论列表
return _reviews;
}
}
// 定义一个私有方法,用于从数据库加载评论
private List<Review> LoadReviewsFromDB()
{
// 这里可以实现数据库查询逻辑,例如使用Entity Framework
// 简单示例,返回一个空列表
return new List<Review>();
}
}
// 定义评论类
public class Review
{
// 可以定义评论的相关属性,例如评论内容、评分等
}
3. 日志监控系统
日志监控系统可以记录程序的运行状态和关键操作,方便后续的调试和分析。通过封装日志记录逻辑,可以在不影响原有业务逻辑的情况下添加日志功能。
// 定义一个Account类
public class Account
{
// 定义一个私有字段,用于存储账户的余额
private decimal _balance;
// 定义一个公共属性,用于对外提供对余额的访问和设置
public decimal Balance
{
// get访问器,用于获取余额的值,并记录日志
get
{
// 调用Logger类的Log方法记录查询余额的日志
Logger.Log($"查询余额:{_balance}");
// 返回余额的值
return _balance;
}
// set访问器,用于设置余额的值,并记录日志
set
{
// 调用Logger类的Log方法记录修改余额的日志
Logger.Log($"修改余额:{_balance} → {value}");
// 将输入的余额赋值给私有字段
_balance = value;
}
}
}
// 定义日志记录类
// 静态类表示该类不能被实例化,只能通过类名直接调用其静态成员
public static class Logger
{
// 定义一个静态方法,用于记录日志
public static void Log(string message)
{
// 这里可以实现日志记录逻辑,例如将日志写入文件或数据库
// 简单示例,将日志输出到控制台
Console.WriteLine(message);
}
}
4. 安全权限控制
在一些应用中,我们需要对某些操作进行权限控制,只有具备相应权限的用户才能执行这些操作。通过封装权限验证逻辑,可以确保系统的安全性。
// 定义一个AdminPanel类
public class AdminPanel
{
// 定义一个私有字段,用于存储是否已登录的状态
private bool _isLoggedIn;
// 定义一个公共方法,用于执行需要权限控制的操作
public void AccessControl()
{
// 如果未登录,则抛出未授权访问异常
if (!_isLoggedIn)
throw new UnauthorizedAccessException();
// 执行敏感操作,例如修改系统配置等
// 这里可以添加具体的业务逻辑
Console.WriteLine("执行敏感操作");
}
}
四、封装的注意事项
1. 避免过度设计
过度设计是指在代码中添加了过多不必要的封装和抽象,导致代码变得复杂难懂,增加了维护成本。下面是一个反模式示例及改进方案:
// 反模式示例
public class BadExample
{
// 定义一个公共方法,用于获取数据
public void GetData()
{
// 调用私有方法执行步骤1
Step1();
// 调用私有方法执行步骤2
Step2();
// 调用私有方法执行步骤3
Step3();
}
// 定义一个私有方法,用于执行步骤1
private void Step1() { }
// 定义一个私有方法,用于执行步骤2
private void Step2() { }
// 定义一个私有方法,用于执行步骤3
private void Step3() { }
}
// 改进方案
public class GoodExample
{
// 定义一个公共方法,用于获取数据
public void GetData()
{
// 调用私有方法执行所有步骤
PerformAllSteps();
}
// 定义一个私有方法,将相关步骤合并执行
private void PerformAllSteps()
{
// 这里可以将步骤1、2、3的逻辑合并
}
}
判断是否过度设计的一个简单方法是看代码是否为了封装而封装,是否增加了不必要的复杂性。如果一个功能可以用简单直接的方式实现,就不需要过度封装。
2. 合理暴露接口
遵循最小知识原则,只提供必要的公共方法和属性。这样可以减少外部代码对类内部实现的依赖,提高代码的可维护性。例如,一个类只需要提供一个计算结果的方法,而不需要将计算过程中的中间变量暴露给外部。
3. 性能优化考量
避免在属性中执行复杂计算。属性应该是轻量级的访问器,只用于获取或设置数据。如果需要执行复杂计算,应该使用方法来实现。
// 反模式
public class BadPerformanceExample
{
// 定义一个公共属性,在get访问器中执行复杂计算
public decimal TotalPrice
{
get { return CalculateTotal(); }
}
// 定义一个私有方法,用于计算总价
private decimal CalculateTotal()
{
// 这里可以实现复杂的计算逻辑
return 0;
}
}
// 推荐方式
public class GoodPerformanceExample
{
// 定义一个公共方法,用于获取总价
public decimal GetTotalPrice()
{
return CalculateTotal();
}
// 定义一个私有方法,用于计算总价
private decimal CalculateTotal()
{
// 这里可以实现复杂的计算逻辑
return 0;
}
}
4. 封装与继承的平衡
子类不应直接访问父类私有成员,而是通过公共接口交互。这样可以保证父类的封装性,避免子类对父类内部实现的过度依赖。
五、封装与设计模式的结合
1. 工厂模式
工厂模式的目的是将对象的创建和使用分离,提供一个统一的接口来创建对象,从而提高代码的可维护性和可扩展性。例如,当我们需要根据不同的条件创建不同类型的对象时,使用工厂模式可以避免在业务代码中出现大量的条件判断语句。
// 定义交通工具类型的枚举
public enum VehicleType
{
Car,
Truck
}
// 定义交通工具接口
public interface IVehicle
{
// 定义一个方法,用于启动交通工具
void Start();
}
// 定义汽车类,实现交通工具接口
public class Car : IVehicle
{
// 实现启动方法
public void Start()
{
Console.WriteLine("汽车启动");
}
}
// 定义卡车类,实现交通工具接口
public class Truck : IVehicle
{
// 实现启动方法
public void Start()
{
Console.WriteLine("卡车启动");
}
}
// 定义交通工具工厂类
public class VehicleFactory
{
// 定义一个静态方法,用于根据交通工具类型创建具体的交通工具实例
public static IVehicle CreateVehicle(VehicleType type)
{
return type switch
{
// 如果是汽车类型,则创建汽车实例
VehicleType.Car => new Car(),
// 如果是卡车类型,则创建卡车实例
VehicleType.Truck => new Truck(),
// 如果是其他类型,则抛出参数异常
_ => throw new ArgumentException()
};
}
}
// 使用示例
class Program
{
static void Main()
{
// 通过工厂类创建汽车实例
IVehicle car = VehicleFactory.CreateVehicle(VehicleType.Car);
car.Start();
// 通过工厂类创建卡车实例
IVehicle truck = VehicleFactory.CreateVehicle(VehicleType.Truck);
truck.Start();
}
}
2. 策略模式
策略模式允许在运行时选择不同的算法或行为。它将每个算法封装成一个独立的类,这些类实现同一个接口,然后在需要使用算法的地方可以根据不同的需求动态切换算法。这样可以提高代码的灵活性和可维护性。
// 定义支付策略接口
public interface IPaymentStrategy
{
// 定义一个方法,用于处理支付
void ProcessPayment(decimal amount);
}
// 定义信用卡支付类,实现支付策略接口
public class CreditCardPayment : IPaymentStrategy
{
// 实现处理支付的方法
public void ProcessPayment(decimal amount)
{
// 实现信用卡支付的具体逻辑
Console.WriteLine($"使用信用卡支付了 {amount} 元");
}
}
// 定义支付宝支付类,实现支付策略接口
public class AlipayPayment : IPaymentStrategy
{
// 实现处理支付的方法
public void ProcessPayment(decimal amount)
{
// 实现支付宝支付的具体逻辑
Console.WriteLine($"使用支付宝支付了 {amount} 元");
}
}
// 定义订单类,使用支付策略
public class Order
{
// 定义一个私有字段,用于存储支付策略
private IPaymentStrategy _paymentStrategy;
// 定义一个公共方法,用于设置支付策略
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
// 定义一个公共方法,用于处理订单支付
public void Pay(decimal amount)
{
// 调用支付策略的处理支付方法
_paymentStrategy.ProcessPayment(amount);
}
}
3. 装饰器模式
// 定义订单接口
public interface IOrder
{
// 定义一个方法,用于下单
void PlaceOrder();
}
// 定义具体订单类,实现订单接口
public class ConcreteOrder : IOrder
{
// 实现下单方法
public void PlaceOrder()
{
Console.WriteLine("下单操作");
}
}
// 定义日志记录订单类,实现订单接口
public class LoggedOrder : IOrder
{
// 定义一个私有字段,用于存储被装饰的订单对象
private readonly IOrder _order;
// 构造函数,接收被装饰的订单对象
public LoggedOrder(IOrder order)
{
_order = order;
}
// 实现下单方法,在下单前后记录日志
public void PlaceOrder()
{
// 记录下单开始的日志
Logger.Log("下单开始");
// 调用被装饰订单对象的下单方法
_order.PlaceOrder();
// 记录下单完成的日志
Logger.Log("下单完成");
}
}
六、常见面试问题解答
Q1:封装和数据隐藏的区别?
A:封装是更广泛的概念,包含数据隐藏和方法聚合。数据隐藏是封装的一部分。
Q2:属性和字段的区别?
A:属性是字段的保护层,允许在读写时执行逻辑,而字段是直接存储数据的变量。
Q3:何时使用 protected vs internal?
A:protected 用于继承场景,internal 用于程序集内部访问。
Q4:如何实现只读属性?
A:只提供 get 访问器,或在 set 中抛出异常。
七、项目实战
实战项目1:用户管理系统
// 定义用户权限枚举,包含普通用户和管理员两种角色
public enum UserRole
{
NormalUser,
Administrator
}
// 用户类,用于封装用户的基本信息和操作
public class User
{
// 私有字段,用于存储用户的姓名
private string _name;
// 私有字段,用于存储用户的邮箱
private string _email;
// 私有字段,用于存储用户的密码
private string _password;
// 私有字段,用于存储用户的角色
private UserRole _role;
// 公共属性 - 姓名
// get访问器:返回用户的姓名
// set访问器:对输入的姓名进行验证,若为空则抛出异常,否则将其赋值给私有字段
public string Name
{
get => _name;
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("姓名不能为空");
_name = value;
}
}
// 公共属性 - 邮箱
// get访问器:返回用户的邮箱
// set访问器:对输入的邮箱进行格式验证,若格式不正确则抛出异常,否则将其赋值给私有字段
public string Email
{
get => _email;
set
{
if (!IsValidEmail(value))
throw new FormatException("邮箱格式不正确");
_email = value;
}
}
// 公共属性 - 密码(示例仅做基础验证)
// get访问器:返回用户的密码
// set访问器:对输入的密码进行长度验证,若长度小于6位则抛出异常,否则将其赋值给私有字段
public string Password
{
get => _password;
set
{
if (value.Length < 6)
throw new ArgumentException("密码长度至少6位");
_password = value;
}
}
// 公共属性 - 用户角色
// get访问器:返回用户的角色
// set访问器:进行权限控制,只有当前登录用户为管理员时才能设置用户角色,否则抛出异常
public UserRole Role
{
get => _role;
set
{
// 权限控制:只有管理员可以设置其他用户角色
if (CurrentUser?.Role != UserRole.Administrator)
throw new UnauthorizedAccessException();
_role = value;
}
}
// 私有验证方法 - 邮箱格式
// 简单判断邮箱中是否包含@符号,用于验证邮箱格式是否正确
private bool IsValidEmail(string email)
{
return email.Contains("@"); // 简化验证逻辑
}
// 静态属性 - 当前登录用户(模拟单例)
// 用于存储当前登录的用户信息
public static User CurrentUser { get; private set; }
// 登录方法
// 模拟用户登录过程,将传入的邮箱和密码创建一个用户对象并赋值给当前登录用户
public static void Login(string email, string password)
{
// 模拟数据库查询
var user = new User { Email = email, Password = password };
CurrentUser = user;
Console.WriteLine("登录成功");
}
// 注销方法
// 将当前登录用户置为null,表示用户已注销
public static void Logout()
{
CurrentUser = null;
Console.WriteLine("已注销");
}
}
// 使用示例
class Program
{
static void Main()
{
try
{
// 模拟管理员用户登录
User.Login("admin@example.com", "admin123");
// 创建一个新用户对象,并设置其姓名、邮箱和密码
var user = new User
{
Name = "张三",
Email = "zhangsan@example.com",
Password = "pass123"
};
// 管理员设置角色
user.Role = UserRole.NormalUser;
// 输出新用户创建成功的信息,包括用户姓名和角色
Console.WriteLine($"用户 {user.Name} 创建成功,角色:{user.Role}");
}
catch (Exception ex)
{
// 捕获并输出可能出现的异常信息
Console.WriteLine($"错误:{ex.Message}");
}
}
}
实战项目2:订单系统
// 订单项类,用于表示订单中的一个商品项
public class OrderItem
{
// 商品名称属性,用于存储订单项中商品的名称
public string ProductName { get; set; }
// 商品价格属性,用于存储订单项中商品的单价
public decimal Price { get; set; }
// 商品数量属性,用于存储订单项中商品的购买数量
public int Quantity { get; set; }
}
// 折扣策略接口,定义了应用折扣的方法
public interface IDiscountStrategy
{
// 应用折扣的方法,接收一个总价作为参数,返回折扣后的价格
decimal ApplyDiscount(decimal total);
}
// 百分比折扣策略类,实现了折扣策略接口
public class PercentageDiscount : IDiscountStrategy
{
// 私有字段,用于存储折扣的百分比
private decimal _percentage;
// 构造函数,接收一个百分比参数并赋值给私有字段
public PercentageDiscount(decimal percentage)
{
_percentage = percentage;
}
// 实现接口中的应用折扣方法,根据百分比计算折扣后的价格
public decimal ApplyDiscount(decimal total)
{
return total * (1 - _percentage / 100);
}
}
// 固定金额折扣策略类,实现了折扣策略接口
public class FixedAmountDiscount : IDiscountStrategy
{
// 私有字段,用于存储固定的折扣金额
private decimal _amount;
// 构造函数,接收一个固定金额参数并赋值给私有字段
public FixedAmountDiscount(decimal amount)
{
_amount = amount;
}
// 实现接口中的应用折扣方法,从总价中减去固定金额得到折扣后的价格
public decimal ApplyDiscount(decimal total)
{
return Math.Max(total - _amount, 0);
}
}
// 订单类,用于管理订单的相关操作
public class Order
{
// 私有字段,用于存储订单项的列表
private List<OrderItem> _items = new List<OrderItem>();
// 私有字段,用于存储当前使用的折扣策略
private IDiscountStrategy _discountStrategy;
// 添加订单项的方法
// 接收一个订单项对象,将其添加到订单项列表中,并记录日志
public void AddItem(OrderItem item)
{
_items.Add(item);
Logger.Log($"添加商品:{item.ProductName} x{item.Quantity}");
}
// 设置折扣策略的方法
// 接收一个折扣策略对象,将其赋值给私有字段
public void SetDiscountStrategy(IDiscountStrategy strategy)
{
_discountStrategy = strategy;
}
// 计算总价的方法
// 先计算所有订单项的总价,记录原价日志
// 如果设置了折扣策略,则应用折扣并记录折扣后价格日志
// 最后返回最终的总价
public decimal CalculateTotal()
{
decimal total = _items.Sum(i => i.Price * i.Quantity);
Logger.Log($"原价:{total:C}");
if (_discountStrategy != null)
{
total = _discountStrategy.ApplyDiscount(total);
Logger.Log($"折扣后价格:{total:C}");
}
return total;
}
}
// 日志记录类,使用静态类实现,方便在不同地方调用日志记录方法
public static class Logger
{
// 静态方法,用于记录日志
// 接收一个日志消息作为参数,将当前时间和消息输出到控制台
public static void Log(string message)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {message}");
}
}
// 使用示例
class Program
{
static void Main()
{
// 创建一个订单对象
var order = new Order();
// 向订单中添加一个笔记本电脑订单项
order.AddItem(new OrderItem
{
ProductName = "笔记本电脑",
Price = 8999,
Quantity = 1
});
// 向订单中添加一个无线鼠标订单项
order.AddItem(new OrderItem
{
ProductName = "无线鼠标",
Price = 199,
Quantity = 2
});
// 应用10%折扣
order.SetDiscountStrategy(new PercentageDiscount(10));
// 计算订单的最终总价
decimal total = order.CalculateTotal();
// 输出订单的最终总价
Console.WriteLine($"最终总价:{total:C}");
}
}
实战项目3:配置管理模块
// 配置项类型枚举,定义了不同类型的配置项
public enum ConfigType
{
DatabaseConnection,
AppSettings
}
// 配置变更事件参数类,继承自EventArgs类
// 用于在配置发生变更时传递相关信息
public class ConfigChangedEventArgs : EventArgs
{
// 配置项的键
public string Key { get; set; }
// 配置项的旧值
public string OldValue { get; set; }
// 配置项的新值
public string NewValue { get; set; }
}
// 配置管理类,用于管理配置项的加载、存储和变更通知
public class ConfigManager
{
// 私有字段,使用字典存储配置项的键值对
private Dictionary<string, string> _configs = new Dictionary<string, string>();
// 私有字段,用于标记配置是否已经加载
private bool _isLoaded = false;
// 获取配置项的方法
// 调用LoadConfigs方法确保配置已加载,然后尝试从字典中获取指定键的配置值
public string GetConfig(string key)
{
LoadConfigs(); // 确保配置已加载
return _configs.TryGetValue(key, out string value) ? value : null;
}
// 设置配置项的方法
// 先获取旧值,然后将新值加密后存储到字典中
// 最后触发配置变更事件
public void SetConfig(string key, string value)
{
var oldValue = _configs.GetValueOrDefault(key);
_configs[key] = Encrypt(value); // 加密存储
OnConfigChanged(key, oldValue, value);
}
// 加载配置的方法
// 如果配置已经加载则直接返回,否则模拟从文件读取配置并加密存储到字典中
// 最后将加载标记置为true并输出加载完成的日志
private void LoadConfigs()
{
if (_isLoaded) return;
// 模拟加载过程
_configs["DatabaseConnection"] = Encrypt("Server=localhost;Database=test");
_configs["AppSettings"] = Encrypt("Theme=Dark;Language=zh-CN");
_isLoaded = true;
Console.WriteLine("配置加载完成");
}
// 加密方法(示例)
// 将输入的字符串转换为字节数组,然后使用Base64编码进行加密
private string Encrypt(string input)
{
return Convert.ToBase64String(Encoding.UTF8.GetBytes(input));
}
// 解密方法(示例)
// 将Base64编码的字符串解码为字节数组,然后转换为字符串
private string Decrypt(string input)
{
return Encoding.UTF8.GetString(Convert.FromBase64String(input));
}
// 配置变更事件,使用EventHandler委托
// 用于在配置发生变更时通知订阅者
public event EventHandler<ConfigChangedEventArgs> ConfigChanged;
// 触发配置变更事件的方法
// 当配置发生变更时,调用此方法触发事件并传递相关信息
protected virtual void OnConfigChanged(string key, string oldValue, string newValue)
{
ConfigChanged?.Invoke(this, new ConfigChangedEventArgs
{
Key = key,
OldValue = Decrypt(oldValue),
NewValue = Decrypt(newValue)
});
}
}
// 使用示例
class Program
{
static void Main()
{
// 创建一个配置管理对象
var config = new ConfigManager();
// 注册事件
// 当配置发生变更时,输出变更信息到控制台
config.ConfigChanged += (sender, e) =>
{
Console.WriteLine($"配置变更:{e.Key} 从 {e.OldValue} 变更为 {e.NewValue}");
};
// 获取配置(自动加载)
// 获取数据库连接配置并解密后输出到控制台
string connStr = config.GetConfig("DatabaseConnection");
Console.WriteLine($"数据库连接:{config.Decrypt(connStr)}");
// 修改配置
// 设置应用程序设置配置,触发配置变更事件
config.SetConfig("AppSettings", "Theme=Light;Language=en-US");
}
}
代码说明
用户管理系统:
使用属性封装字段,实现数据验证
通过静态方法管理当前用户状态
权限控制通过枚举和角色检查实现
订单系统:
使用策略模式实现不同折扣算法
通过日志类记录操作过程
订单类封装业务逻辑,隐藏内部实现
配置管理模块:
懒加载通过LoadConfigs()方法实现
加密存储使用 Base64(实际应使用更安全算法)
事件机制实现配置变更通知
每个项目都包含完整的使用示例,建议从以下步骤开始练习:
复制代码到 Visual Studio 创建新项目
逐行阅读代码,理解每个部分的作用
修改验证逻辑或添加新功能(如用户注册、订单支付)
尝试扩展配置管理模块,支持更多加密方式
通过这些项目可以掌握:
封装的核心实现方式
数据验证与异常处理
简单设计模式的应用
事件与委托的使用
懒加载和单例模式的实现