1. 入门
咱们先来聊聊直接用字段会遇到啥问题。如果你把字段声明成public,那它就能被程序里任何地方直接改:
public class Dog
{
public string Name;
}
Dog dog = new Dog();
dog.Name = ""; // O_o ... 这就像“狗的名字 = 空字符串”!
用户可以给Name字段赋各种离谱的值,比如空字符串、超长名字,甚至null。这就像你让别人组装你的新IKEA柜子,还把整个木工厂都交给他随便折腾。
提醒下,封装就是对象自己管好自己的数据。我们不会让每个人都能随便动内部数据,而是给出专门的“门”——属性(Properties),通过它们访问字段,还能加校验、日志、修改或者别的操作。
2. 定义和语法
属性是类里的特殊成员,看起来像字段,其实底层是两个特殊方法:getter(获取值)和setter(设置值)。用属性你可以:
- 允许(或禁止)读/写数据;
- 访问数据时加校验或逻辑;
- 隐藏内部字段,甚至把值存别的地方。
属性声明和字段很像,只是多了大括号和get、set关键字。
[访问修饰符] 类型 属性名
{
get { ... }
set { ... }
}
属性声明模板
比如我们的Dog类:
public class Dog
{
private string _name; // 内部字段(private!)
public string Name
{
get { return _name; } // “getter”:获取名字
set { _name = value; } // “setter”:设置名字
}
}
注意:下划线通常用在私有字段(_name)。这是C#的风格标准。
3. 属性的工作机制
属性就像“守门员”,站在对象内部数据和外部世界之间。举个例子:
Dog dog = new Dog();
dog.Name = "小花";
Console.WriteLine(dog.Name);
解释下:
- 当程序执行到dog.Name = "小花";时,其实是调用了属性的set方法,你可以在里面加各种校验(比如名字不能是空的)。
- 执行Console.WriteLine(dog.Name);时,会调用get方法,返回当前值,或者动态算一个值。
看起来像普通字段,其实一切都在你掌控中!
4. 为什么属性是“最佳实践”
大多数时候我们不会让对象的内部字段直接暴露。哪怕现在觉得不需要校验,习惯用属性包一层,等以后规则变了就超方便。
赋值时“校验”示例:
public class Dog
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("狗的名字不能为空!");
}
_name = value;
}
}
}
现在这样:
dog.Name = ""; // 会抛异常!
……保护了我们的对象不被赋奇怪的值。
5. 属性:只读、只写和普通属性
有时候你只想让别人看值(比如狗的出生年份,不能改),或者只让设置(很少见,但有时候你想搞点神秘操作)。
- 只读:只写get,不写set。
- 只写:只写set,不写get。
例子:
public class Dog
{
private int _birthYear = 2018;
// 只读
public int BirthYear
{
get { return _birthYear; }
}
// 只写(极少用)
public string Secret
{
set { /* 这里处理value */ }
}
}
6. 属性 vs 字段
| 字段 | 属性 | |
|---|---|---|
| 语法 | |
|
| 访问 | 直接 | 通过get/set |
| 校验 | 没有 | 可以在set/get里加 |
| 可扩展性 | 没有 | 随时能改 |
| IDE集成 | 显示为字段 | 显示为属性(对框架很重要) |
图解:属性的工作流程
sequenceDiagram
participant User as 对象的用户
participant Dog as Dog对象
participant Field as 私有字段 _name
User->>Dog: dog.Name = "小黄"
Dog->>Dog: set Name("小黄")
Dog->>Field: _name = "小黄"
User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: 读取 _name
Dog->>User: 返回 "小黄"
7. 常见错误和细节
来看看这里会踩哪些坑。
常见错误——把字段和属性搞混,不小心把私有数据暴露出去了:
public string name; // 这是字段!到处都能看到,危险!
正确姿势:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
静态属性也有,用来存所有对象共享的值。但要小心用。
有趣:如果属性只有get,没有构造函数,那它根本改不了——这叫不可变属性(immutable property),在现代设计里很常见(我们后面讲record和数据不可变性会详细聊)。
GO TO FULL VERSION