C# 封装和属性
本章将深入探讨面向对象编程的核心原则之一——封装,以及 C# 中属性的高级用法,帮助你构建更安全、更易维护的代码。
封装的概念
什么是封装
csharp
// 封装的基本思想:隐藏内部实现细节,只暴露必要的接口
// ❌ 不好的设计 - 直接暴露字段
public class BadBankAccount
{
public decimal balance; // 直接暴露,任何人都可以修改
public string accountNumber;
public void DisplayBalance()
{
Console.WriteLine($"余额: {balance}");
}
}
// ✅ 好的设计 - 使用封装
public class GoodBankAccount
{
private decimal balance; // 私有字段,隐藏实现
private readonly string accountNumber;
public GoodBankAccount(string accountNumber, decimal initialBalance = 0)
{
this.accountNumber = accountNumber;
this.balance = initialBalance >= 0 ? initialBalance : 0;
}
// 通过方法控制访问
public decimal GetBalance()
{
return balance;
}
public bool Deposit(decimal amount)
{
if (amount > 0)
{
balance += amount;
return true;
}
return false;
}
public bool Withdraw(decimal amount)
{
if (amount > 0 && amount <= balance)
{
balance -= amount;
return true;
}
return false;
}
public string GetAccountNumber()
{
return accountNumber;
}
}
static void EncapsulationDemo()
{
// 使用封装良好的类
var account = new GoodBankAccount("12345", 1000m);
Console.WriteLine($"账号: {account.GetAccountNumber()}");
Console.WriteLine($"余额: {account.GetBalance():C}");
// 通过方法安全地操作数据
account.Deposit(500m);
Console.WriteLine($"存款后余额: {account.GetBalance():C}");
bool success = account.Withdraw(2000m); // 尝试取出超额金额
Console.WriteLine($"取款{(success ? "成功" : "失败")}");
Console.WriteLine($"当前余额: {account.GetBalance():C}");
}封装的好处
csharp
public class Temperature
{
private double celsius;
// 封装的好处1:数据验证
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15) // 绝对零度检查
throw new ArgumentException("温度不能低于绝对零度");
celsius = value;
}
}
// 封装的好处2:计算属性
public double Fahrenheit
{
get { return celsius * 9.0 / 5.0 + 32; }
set { Celsius = (value - 32) * 5.0 / 9.0; }
}
public double Kelvin
{
get { return celsius + 273.15; }
set { Celsius = value - 273.15; }
}
// 封装的好处3:内部状态管理
public string TemperatureCategory
{
get
{
if (celsius < 0) return "冰点以下";
if (celsius < 10) return "寒冷";
if (celsius < 20) return "凉爽";
if (celsius < 30) return "温暖";
return "炎热";
}
}
public Temperature(double celsius = 0)
{
Celsius = celsius; // 使用属性确保验证
}
public void DisplayTemperature()
{
Console.WriteLine($"温度: {Celsius:F1}°C / {Fahrenheit:F1}°F / {Kelvin:F1}K ({TemperatureCategory})");
}
}
static void TemperatureDemo()
{
var temp = new Temperature(25);
temp.DisplayTemperature();
// 通过不同单位设置温度
temp.Fahrenheit = 100; // 设置华氏温度
temp.DisplayTemperature();
temp.Kelvin = 300; // 设置开尔文温度
temp.DisplayTemperature();
try
{
temp.Celsius = -300; // 尝试设置无效温度
}
catch (ArgumentException ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
}属性的深入应用
自动属性的演进
csharp
// C# 3.0 自动属性
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// C# 6.0 自动属性初始化器
public class PersonV2
{
public string Name { get; set; } = "Unknown";
public int Age { get; set; } = 0;
public DateTime CreatedDate { get; } = DateTime.Now; // 只读自动属性
public List<string> Hobbies { get; } = new List<string>(); // 只读集合属性
}
// C# 7.0+ 表达式体属性
public class PersonV3
{
private string firstName;
private string lastName;
private DateTime birthDate;
public string FirstName
{
get => firstName;
set => firstName = value?.Trim();
}
public string LastName
{
get => lastName;
set => lastName = value?.Trim();
}
public DateTime BirthDate
{
get => birthDate;
set => birthDate = value <= DateTime.Now ? value : throw new ArgumentException("出生日期不能是未来时间");
}
// 计算属性
public string FullName => $"{FirstName} {LastName}";
public int Age => DateTime.Now.Year - BirthDate.Year - (DateTime.Now.DayOfYear < BirthDate.DayOfYear ? 1 : 0);
public bool IsAdult => Age >= 18;
public string AgeGroup => Age switch
{
< 13 => "儿童",
< 20 => "青少年",
< 60 => "成年人",
_ => "老年人"
};
}属性访问器的高级用法
csharp
public class SecureProperty
{
private string password;
private int loginAttempts = 0;
private DateTime lastLoginAttempt = DateTime.MinValue;
private bool isLocked = false;
public string Password
{
get
{
// 密码不能直接获取
throw new UnauthorizedAccessException("密码不能直接访问");
}
set
{
// 密码设置时的验证
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("密码不能为空");
if (value.Length < 8)
throw new ArgumentException("密码长度至少8位");
if (!HasUpperCase(value) || !HasLowerCase(value) || !HasDigit(value))
throw new ArgumentException("密码必须包含大小写字母和数字");
password = HashPassword(value); // 存储哈希值
Console.WriteLine("密码设置成功");
}
}
public bool IsLocked
{
get
{
// 检查是否因为多次失败尝试而锁定
if (isLocked && DateTime.Now.Subtract(lastLoginAttempt).TotalMinutes > 30)
{
isLocked = false; // 30分钟后自动解锁
loginAttempts = 0;
}
return isLocked;
}
}
public int RemainingAttempts => Math.Max(0, 3 - loginAttempts);
public bool VerifyPassword(string inputPassword)
{
if (IsLocked)
{
Console.WriteLine("账户已锁定,请稍后再试");
return false;
}
lastLoginAttempt = DateTime.Now;
if (HashPassword(inputPassword) == password)
{
loginAttempts = 0; // 重置失败次数
Console.WriteLine("密码验证成功");
return true;
}
else
{
loginAttempts++;
if (loginAttempts >= 3)
{
isLocked = true;
Console.WriteLine("密码错误次数过多,账户已锁定30分钟");
}
else
{
Console.WriteLine($"密码错误,还有 {RemainingAttempts} 次机会");
}
return false;
}
}
private bool HasUpperCase(string str) => str.Any(char.IsUpper);
private bool HasLowerCase(string str) => str.Any(char.IsLower);
private bool HasDigit(string str) => str.Any(char.IsDigit);
private string HashPassword(string password) => password.GetHashCode().ToString(); // 简化的哈希
}
static void SecurePropertyDemo()
{
var secure = new SecureProperty();
try
{
// 设置密码
secure.Password = "MySecure123";
// 验证密码
secure.VerifyPassword("wrong");
secure.VerifyPassword("MySecure123");
// 尝试直接获取密码
// string pwd = secure.Password; // 这会抛出异常
}
catch (Exception ex)
{
Console.WriteLine($"异常: {ex.Message}");
}
}索引器属性
csharp
public class Matrix
{
private double[,] data;
private int rows;
private int cols;
public Matrix(int rows, int cols)
{
this.rows = rows;
this.cols = cols;
this.data = new double[rows, cols];
}
// 索引器 - 允许像数组一样访问对象
public double this[int row, int col]
{
get
{
if (row < 0 || row >= rows || col < 0 || col >= cols)
throw new IndexOutOfRangeException("索引超出范围");
return data[row, col];
}
set
{
if (row < 0 || row >= rows || col < 0 || col >= cols)
throw new IndexOutOfRangeException("索引超出范围");
data[row, col] = value;
}
}
// 字符串索引器
public double this[string position]
{
get
{
var parts = position.Split(',');
if (parts.Length != 2 ||
!int.TryParse(parts[0], out int row) ||
!int.TryParse(parts[1], out int col))
throw new ArgumentException("位置格式错误,应为 'row,col'");
return this[row, col];
}
set
{
var parts = position.Split(',');
if (parts.Length != 2 ||
!int.TryParse(parts[0], out int row) ||
!int.TryParse(parts[1], out int col))
throw new ArgumentException("位置格式错误,应为 'row,col'");
this[row, col] = value;
}
}
public int Rows => rows;
public int Cols => cols;
public void Fill(double value)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
data[i, j] = value;
}
}
}
public void DisplayMatrix()
{
Console.WriteLine($"Matrix ({rows}x{cols}):");
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Console.Write($"{data[i, j],8:F2}");
}
Console.WriteLine();
}
}
}
public class Dictionary<TKey, TValue>
{
private List<KeyValuePair<TKey, TValue>> items = new List<KeyValuePair<TKey, TValue>>();
// 索引器实现字典功能
public TValue this[TKey key]
{
get
{
var item = items.FirstOrDefault(kvp => kvp.Key.Equals(key));
if (item.Equals(default(KeyValuePair<TKey, TValue>)))
throw new KeyNotFoundException($"键 '{key}' 不存在");
return item.Value;
}
set
{
var index = items.FindIndex(kvp => kvp.Key.Equals(key));
if (index >= 0)
{
items[index] = new KeyValuePair<TKey, TValue>(key, value);
}
else
{
items.Add(new KeyValuePair<TKey, TValue>(key, value));
}
}
}
public bool ContainsKey(TKey key)
{
return items.Any(kvp => kvp.Key.Equals(key));
}
public void Remove(TKey key)
{
items.RemoveAll(kvp => kvp.Key.Equals(key));
}
public IEnumerable<TKey> Keys => items.Select(kvp => kvp.Key);
public IEnumerable<TValue> Values => items.Select(kvp => kvp.Value);
public int Count => items.Count;
}
static void IndexerDemo()
{
// 矩阵索引器
var matrix = new Matrix(3, 3);
// 使用数字索引器
matrix[0, 0] = 1.0;
matrix[1, 1] = 2.0;
matrix[2, 2] = 3.0;
// 使用字符串索引器
matrix["0,1"] = 0.5;
matrix["1,0"] = 0.5;
matrix.DisplayMatrix();
Console.WriteLine($"matrix[1,1] = {matrix[1, 1]}");
Console.WriteLine($"matrix['0,1'] = {matrix["0,1"]}");
// 自定义字典索引器
var dict = new Dictionary<string, int>();
dict["apple"] = 5;
dict["banana"] = 3;
dict["orange"] = 8;
Console.WriteLine($"\n字典内容:");
foreach (var key in dict.Keys)
{
Console.WriteLine($"{key}: {dict[key]}");
}
}字段与属性的选择
何时使用字段,何时使用属性
csharp
public class FieldVsPropertyDemo
{
// ✅ 使用字段的情况:
// 1. 常量
public const double PI = 3.14159265359;
// 2. 静态只读字段
public static readonly DateTime ApplicationStartTime = DateTime.Now;
// 3. 私有实现细节
private List<string> items = new List<string>();
private int cacheVersion = 0;
// ✅ 使用属性的情况:
// 1. 公共数据访问
public string Name { get; set; }
// 2. 需要验证的数据
private int age;
public int Age
{
get => age;
set => age = value >= 0 ? value : throw new ArgumentException("年龄不能为负数");
}
// 3. 计算属性
public bool IsAdult => Age >= 18;
// 4. 延迟计算
private string expensiveCalculationResult;
public string ExpensiveCalculation
{
get
{
if (expensiveCalculationResult == null)
{
// 模拟昂贵的计算
Thread.Sleep(100);
expensiveCalculationResult = $"计算结果_{DateTime.Now.Ticks}";
}
return expensiveCalculationResult;
}
}
// 5. 通知属性更改
public event PropertyChangedEventHandler PropertyChanged;
private string description;
public string Description
{
get => description;
set
{
if (description != value)
{
description = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// 6. 版本控制和缓存失效
private string cachedData;
public string CachedData
{
get
{
if (cachedData == null || cacheVersion < GetCurrentVersion())
{
cachedData = LoadData();
cacheVersion = GetCurrentVersion();
}
return cachedData;
}
}
private string LoadData() => $"数据_{DateTime.Now.Millisecond}";
private int GetCurrentVersion() => DateTime.Now.Second;
}
// 属性更改通知接口
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
public class PropertyChangedEventArgs : EventArgs
{
public string PropertyName { get; }
public PropertyChangedEventArgs(string propertyName)
{
PropertyName = propertyName;
}
}
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
// CallerMemberName 属性
public class CallerMemberNameAttribute : Attribute { }实践示例
示例 1:配置管理类
csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
public class ConfigurationManager
{
private static ConfigurationManager instance;
private static readonly object lockObject = new object();
private Dictionary<string, object> settings;
private string configFilePath;
private DateTime lastModified;
// 单例模式
public static ConfigurationManager Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
instance = new ConfigurationManager();
}
}
return instance;
}
}
private ConfigurationManager()
{
configFilePath = "config.json";
settings = new Dictionary<string, object>();
LoadConfiguration();
}
// 索引器用于访问配置项
public T GetValue<T>(string key, T defaultValue = default(T))
{
CheckForConfigChanges();
if (settings.TryGetValue(key, out object value))
{
try
{
if (value is JsonElement jsonElement)
{
return JsonSerializer.Deserialize<T>(jsonElement.GetRawText());
}
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return defaultValue;
}
}
return defaultValue;
}
public void SetValue<T>(string key, T value)
{
CheckForConfigChanges();
settings[key] = value;
SaveConfiguration();
}
// 强类型配置属性
public string DatabaseConnectionString
{
get => GetValue<string>("DatabaseConnectionString", "Server=localhost;Database=MyApp;");
set => SetValue("DatabaseConnectionString", value);
}
public int MaxRetryAttempts
{
get => GetValue<int>("MaxRetryAttempts", 3);
set => SetValue("MaxRetryAttempts", Math.Max(1, value));
}
public TimeSpan RequestTimeout
{
get => TimeSpan.FromSeconds(GetValue<double>("RequestTimeoutSeconds", 30.0));
set => SetValue("RequestTimeoutSeconds", value.TotalSeconds);
}
public bool EnableLogging
{
get => GetValue<bool>("EnableLogging", true);
set => SetValue("EnableLogging", value);
}
public LogLevel LogLevel
{
get => Enum.Parse<LogLevel>(GetValue<string>("LogLevel", "Information"));
set => SetValue("LogLevel", value.ToString());
}
// 嵌套配置对象
public EmailSettings Email => new EmailSettings(this);
private void LoadConfiguration()
{
try
{
if (File.Exists(configFilePath))
{
string json = File.ReadAllText(configFilePath);
settings = JsonSerializer.Deserialize<Dictionary<string, object>>(json) ?? new Dictionary<string, object>();
lastModified = File.GetLastWriteTime(configFilePath);
}
}
catch (Exception ex)
{
Console.WriteLine($"加载配置文件失败: {ex.Message}");
settings = new Dictionary<string, object>();
}
}
private void SaveConfiguration()
{
try
{
string json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(configFilePath, json);
lastModified = File.GetLastWriteTime(configFilePath);
}
catch (Exception ex)
{
Console.WriteLine($"保存配置文件失败: {ex.Message}");
}
}
private void CheckForConfigChanges()
{
if (File.Exists(configFilePath))
{
var currentModified = File.GetLastWriteTime(configFilePath);
if (currentModified > lastModified)
{
LoadConfiguration();
}
}
}
public void ReloadConfiguration()
{
LoadConfiguration();
}
public IReadOnlyDictionary<string, object> GetAllSettings()
{
CheckForConfigChanges();
return new Dictionary<string, object>(settings);
}
}
public class EmailSettings
{
private readonly ConfigurationManager config;
internal EmailSettings(ConfigurationManager config)
{
this.config = config;
}
public string SmtpServer
{
get => config.GetValue<string>("Email.SmtpServer", "smtp.gmail.com");
set => config.SetValue("Email.SmtpServer", value);
}
public int SmtpPort
{
get => config.GetValue<int>("Email.SmtpPort", 587);
set => config.SetValue("Email.SmtpPort", value);
}
public string Username
{
get => config.GetValue<string>("Email.Username", "");
set => config.SetValue("Email.Username", value);
}
public bool EnableSsl
{
get => config.GetValue<bool>("Email.EnableSsl", true);
set => config.SetValue("Email.EnableSsl", value);
}
}
public enum LogLevel
{
Debug,
Information,
Warning,
Error,
Critical
}
static void ConfigurationDemo()
{
var config = ConfigurationManager.Instance;
// 使用强类型属性
config.DatabaseConnectionString = "Server=prod-server;Database=MyApp;";
config.MaxRetryAttempts = 5;
config.RequestTimeout = TimeSpan.FromMinutes(2);
config.EnableLogging = true;
config.LogLevel = LogLevel.Warning;
// 使用嵌套配置
config.Email.SmtpServer = "smtp.company.com";
config.Email.SmtpPort = 25;
config.Email.EnableSsl = false;
// 使用泛型方法
config.SetValue("CustomSetting", "CustomValue");
string customValue = config.GetValue<string>("CustomSetting");
Console.WriteLine("当前配置:");
Console.WriteLine($"数据库连接: {config.DatabaseConnectionString}");
Console.WriteLine($"最大重试次数: {config.MaxRetryAttempts}");
Console.WriteLine($"请求超时: {config.RequestTimeout}");
Console.WriteLine($"启用日志: {config.EnableLogging}");
Console.WriteLine($"日志级别: {config.LogLevel}");
Console.WriteLine($"SMTP服务器: {config.Email.SmtpServer}");
Console.WriteLine($"自定义设置: {customValue}");
// 显示所有设置
Console.WriteLine("\n所有配置项:");
foreach (var setting in config.GetAllSettings())
{
Console.WriteLine($" {setting.Key}: {setting.Value}");
}
}示例 2:数据验证和绑定
csharp
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
public class ValidationAttribute : Attribute
{
public string ErrorMessage { get; set; }
}
public class RequiredAttribute : ValidationAttribute
{
public RequiredAttribute()
{
ErrorMessage = "此字段为必填项";
}
}
public class RangeAttribute : ValidationAttribute
{
public double Minimum { get; }
public double Maximum { get; }
public RangeAttribute(double minimum, double maximum)
{
Minimum = minimum;
Maximum = maximum;
ErrorMessage = $"值必须在 {minimum} 到 {maximum} 之间";
}
}
public class EmailAttribute : ValidationAttribute
{
public EmailAttribute()
{
ErrorMessage = "请输入有效的邮箱地址";
}
}
public class ValidatableObject : INotifyPropertyChanged, IDataErrorInfo
{
private Dictionary<string, string> errors = new Dictionary<string, string>();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
ValidateProperty(propertyName);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private void ValidateProperty(string propertyName)
{
var property = GetType().GetProperty(propertyName);
if (property == null) return;
var value = property.GetValue(this);
var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), true)
.Cast<ValidationAttribute>();
errors.Remove(propertyName);
foreach (var attribute in attributes)
{
bool isValid = attribute switch
{
RequiredAttribute => value != null && !string.IsNullOrWhiteSpace(value.ToString()),
RangeAttribute range when value is IComparable comparable =>
comparable.CompareTo(range.Minimum) >= 0 && comparable.CompareTo(range.Maximum) <= 0,
EmailAttribute => value?.ToString()?.Contains("@") == true && value.ToString().Contains("."),
_ => true
};
if (!isValid)
{
errors[propertyName] = attribute.ErrorMessage;
break;
}
}
}
public string Error => string.Join("; ", errors.Values);
public string this[string columnName] => errors.TryGetValue(columnName, out string error) ? error : null;
public bool IsValid => !errors.Any();
public Dictionary<string, string> GetAllErrors() => new Dictionary<string, string>(errors);
}
public class User : ValidatableObject
{
private string firstName;
private string lastName;
private string email;
private int age;
private decimal salary;
[Required]
public string FirstName
{
get => firstName;
set => SetProperty(ref firstName, value);
}
[Required]
public string LastName
{
get => lastName;
set => SetProperty(ref lastName, value);
}
[Required]
[Email]
public string Email
{
get => email;
set => SetProperty(ref email, value);
}
[Range(0, 150)]
public int Age
{
get => age;
set => SetProperty(ref age, value);
}
[Range(0, 1000000)]
public decimal Salary
{
get => salary;
set => SetProperty(ref salary, value);
}
// 计算属性
public string FullName => $"{FirstName} {LastName}".Trim();
public bool IsAdult => Age >= 18;
public string SalaryCategory => Salary switch
{
< 30000 => "入门级",
< 60000 => "中级",
< 100000 => "高级",
_ => "专家级"
};
public override string ToString()
{
return $"{FullName} ({Age}岁) - {Email} - {Salary:C} ({SalaryCategory})";
}
}
// 接口定义
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
public interface IDataErrorInfo
{
string Error { get; }
string this[string columnName] { get; }
}
public class PropertyChangedEventArgs : EventArgs
{
public string PropertyName { get; }
public PropertyChangedEventArgs(string propertyName)
{
PropertyName = propertyName;
}
}
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
public class CallerMemberNameAttribute : Attribute { }
static void ValidationDemo()
{
var user = new User();
// 订阅属性更改事件
user.PropertyChanged += (sender, e) =>
{
Console.WriteLine($"属性 '{e.PropertyName}' 已更改");
var error = user[e.PropertyName];
if (!string.IsNullOrEmpty(error))
{
Console.WriteLine($" 验证错误: {error}");
}
};
Console.WriteLine("=== 用户数据验证演示 ===");
// 设置有效数据
Console.WriteLine("\n设置有效数据:");
user.FirstName = "张";
user.LastName = "三";
user.Email = "zhangsan@example.com";
user.Age = 28;
user.Salary = 75000m;
Console.WriteLine($"用户信息: {user}");
Console.WriteLine($"数据有效性: {user.IsValid}");
// 设置无效数据
Console.WriteLine("\n设置无效数据:");
user.FirstName = ""; // 必填项为空
user.Email = "invalid-email"; // 无效邮箱
user.Age = 200; // 超出范围
user.Salary = -1000; // 负数薪资
Console.WriteLine($"数据有效性: {user.IsValid}");
Console.WriteLine("所有验证错误:");
foreach (var error in user.GetAllErrors())
{
Console.WriteLine($" {error.Key}: {error.Value}");
}
// 修正数据
Console.WriteLine("\n修正数据:");
user.FirstName = "李";
user.Email = "lisi@example.com";
user.Age = 30;
user.Salary = 80000m;
Console.WriteLine($"修正后用户信息: {user}");
Console.WriteLine($"数据有效性: {user.IsValid}");
}本章小结
本章深入探讨了 C# 中的封装和属性:
关键要点:
- 封装原则:隐藏实现细节,提供受控的访问接口
- 属性优势:数据验证、计算属性、延迟加载、通知机制
- 索引器:使对象支持类似数组或字典的访问语法
- 字段vs属性:根据使用场景选择合适的数据暴露方式
封装的好处:
- 数据安全:防止无效数据破坏对象状态
- 代码维护:内部实现变更不影响外部代码
- 功能扩展:可以在不改变接口的情况下添加功能
- 调试支持:可以在属性访问时添加日志和断点
最佳实践:
- 优先使用属性而不是公共字段
- 为属性添加适当的验证逻辑
- 使用只读属性暴露计算结果
- 实现 INotifyPropertyChanged 支持数据绑定
- 合理使用索引器简化对象访问
设计原则:
- 最小权限原则:只暴露必要的接口
- 单一职责:每个属性应该有明确的职责
- 开闭原则:对扩展开放,对修改关闭
下一步: 在下一章中,我们将学习继承,了解如何通过继承实现代码重用和扩展。