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