C# 封装(Encapsulation)详解
封装(Encapsulation)是面向对象编程(OOP)的四大特性之一,另外三个是:
- 继承(Inheritance)
- 多态(Polymorphism)
- 抽象(Abstraction)
封装的核心思想:
隐藏对象内部实现细节,只向外暴露必要的功能接口。
简单理解:
- 数据(字段)放在类内部
- 外部不能随意修改
- 通过属性、方法控制访问
一、为什么需要封装
假设有一个学生类:
class Student { public int Age; }使用:
Student stu = new Student(); stu.Age = -100;虽然语法正确,但年龄不可能是负数。
为了防止非法数据,需要封装。
二、使用 private 封装字段
class Student { private int age; public void SetAge(int value) { if (value > 0) { age = value; } } public int GetAge() { return age; } }调用:
Student stu = new Student(); stu.SetAge(18); Console.WriteLine(stu.GetAge());结果:
18三、访问修饰符
C# 使用访问修饰符实现封装。
| 修饰符 | 访问范围 |
|---|---|
| public | 任何地方 |
| private | 当前类内部 |
| protected | 当前类和子类 |
| internal | 当前程序集 |
| protected internal | 当前程序集或子类 |
示例:
class Person { public string Name; private int age; }外部:
Person p = new Person(); p.Name = "Tom"; //可以 p.age = 18; //错误四、属性(Property)封装
实际开发最常用。
传统写法
class Student { private int age; public int Age { get { return age; } set { if (value > 0) { age = value; } } } }使用:
Student stu = new Student(); stu.Age = 20; Console.WriteLine(stu.Age);输出:
20五、get 和 set
get
读取属性值
Console.WriteLine(stu.Age);执行:
get { return age; }set
赋值属性值
stu.Age = 18;执行:
set { age = value; }其中:
value表示赋给属性的值。
例如:
stu.Age = 18;那么:
value == 18六、自动属性
如果不需要验证逻辑:
class Student { public int Age { get; set; } }编译器自动生成私有字段。
使用:
Student stu = new Student(); stu.Age = 18;七、只读属性
方式1
public string Name { get; }只能读取:
public class Student { public string Name { get; } public Student(string name) { Name = name; } }方式2
public string Name { get; private set; }外部只能读:
Student stu = new Student(); Console.WriteLine(stu.Name); // stu.Name = "Tom"; 错误类内部可修改:
Name = "Jerry";八、封装业务逻辑
例如银行卡余额:
class BankAccount { private decimal balance; public decimal Balance { get { return balance; } } public void Deposit(decimal money) { if (money > 0) { balance += money; } } public bool Withdraw(decimal money) { if (money <= balance) { balance -= money; return true; } return false; } }使用:
BankAccount account = new BankAccount(); account.Deposit(1000); account.Withdraw(300); Console.WriteLine(account.Balance);结果:
700优点:
- 防止余额被随意修改
- 保证业务规则正确
- 数据更安全
九、封装与字段的区别
❌ 不推荐:
public string Name;任何人都能直接修改:
obj.Name = ""; obj.Name = null;✅ 推荐:
private string name; public string Name { get { return name; } set { if (!string.IsNullOrEmpty(value)) { name = value; } } }十、完整案例
using System; class Employee { private string name; private decimal salary; public string Name { get { return name; } set { if (!string.IsNullOrWhiteSpace(value)) { name = value; } } } public decimal Salary { get { return salary; } set { if (value >= 0) { salary = value; } } } public void ShowInfo() { Console.WriteLine($"姓名:{Name}"); Console.WriteLine($"工资:{Salary}"); } } class Program { static void Main() { Employee emp = new Employee(); emp.Name = "张三"; emp.Salary = 8000; emp.ShowInfo(); } }输出:
姓名:张三 工资:8000实际开发最佳实践
现代 C#(C# 8+)中推荐:
public class User { public string UserName { get; set; } public int Age { get; private set; } public User(string userName, int age) { UserName = userName; if (age < 0) throw new ArgumentException("年龄不能小于0"); Age = age; } }原则:
- 字段尽量使用
private - 对外优先暴露属性(Property)
- 在
set中做数据验证 - 重要业务数据通过方法操作,不直接开放
set - 保持“高内聚、低耦合”,隐藏实现细节
一句话总结:
C# 封装 =private 字段 + Property 属性 + 方法控制访问,目的是保护数据安全、隐藏实现细节、保证对象状态合法。