C# 封装

访问修饰符、属性、以及为什么要用属性而不是公共字段。

Beginner
Key Points字段私有、属性公开——这样以后加校验或改实现不会破坏调用方。

访问修饰符

ModifierVisible to
public任何代码
private当前类(默认)
protected当前类及派生类
internal当前程序集
protected internal程序集内 或 派生类
private protected程序集内的派生类
C#
class Account
{
    private decimal _balance;          // 只有自己能碰
    public string Owner { get; }       // 外部只读
    internal int Id;                   // 同程序集可见

    public Account(string owner, decimal initial)
    {
        Owner = owner;
        _balance = initial;
    }

    public decimal Balance => _balance;

    public void Deposit(decimal amount)
    {
        if (amount <= 0) throw new ArgumentException("金额必须为正");
        _balance += amount;
    }
}

var a = new Account("张三", 100);
a.Deposit(50);
Console.WriteLine($"{a.Owner} {a.Balance}");
// a._balance = 999;   // 编译错误:私有
Output
张三 150

属性

C#
class User
{
    private string _name = "";

    // 完整属性:带校验
    public string Name
    {
        get => _name;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("名字不能为空");
            _name = value.Trim();
        }
    }

    // 自动属性
    public int Age { get; set; }

    // 只读自动属性(只能在构造函数赋值)
    public string Id { get; }

    // 计算属性
    public bool IsAdult => Age >= 18;

    // 初始化器
    public List<string> Tags { get; } = new();

    public User(string id) => Id = id;
}

var u = new User("u1") { Name = "  李四  ", Age = 20 };
Console.WriteLine($"{u.Id} {u.Name} {u.IsAdult}");
Output
u1 李四 True

A property is sugar for get_X()/set_X() methods, so it costs nothing (except auto-properties, which have a hidden field) and can later gain logic without breaking binary compatibility.

为什么不用公共字段

C#
// 反例:公共字段,以后想加校验就得改所有调用方
class Bad
{
    public int Age;          // 谁都能写 -5
}

// 正例:属性,改实现不影响调用方
class Good
{
    private int _age;
    public int Age
    {
        get => _age;
        set => _age = value < 0 ? 0 : value;
    }
}

var g = new Good { Age = -5 };
Console.WriteLine(g.Age);
Output
0
Turning a public field into a property is a breaking change for both source and binary. Start with a property even if it is just { get; set; } — the most basic and most ignored C# rule.

init 与 required

C#
class Order
{
    public required string Id { get; init; }      // 必须初始化
    public decimal Amount { get; init; }          // 只能在初始化时赋值
    public DateTime Created { get; init; } = DateTime.Now;
}

var o = new Order { Id = "A001", Amount = 99.9m };
Console.WriteLine($"{o.Id} {o.Amount}");
// o.Amount = 1;      // 编译错误:init 只能在初始化器里赋值
Output
A001 99.9

init allows assignment only in an object initializer — more flexible than readonly, safer than set. required forces the caller to assign, checked at compile time.

Friends & Partners