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; } }

原则:

  1. 字段尽量使用private
  2. 对外优先暴露属性(Property)
  3. set中做数据验证
  4. 重要业务数据通过方法操作,不直接开放set
  5. 保持“高内聚、低耦合”,隐藏实现细节

一句话总结:

C# 封装 =private 字段 + Property 属性 + 方法控制访问,目的是保护数据安全、隐藏实现细节、保证对象状态合法。