C Sharp 属性访问器
外观
C#属性访问器[编辑 | 编辑源代码]
属性访问器(Property Accessors)是C#中控制对类字段进行安全读写操作的特殊方法,通过get和set关键字定义。它们封装了字段的访问逻辑,是面向对象编程中封装性的核心实现方式之一。
基本概念[编辑 | 编辑源代码]
属性访问器由以下两部分组成:
- get访问器:读取属性值时调用(相当于字段的读取方法)
- set访问器:设置属性值时调用(相当于字段的赋值方法)
基础语法结构:
public 数据类型 属性名
{
get { return 字段; } // 读取逻辑
set { 字段 = value; } // 赋值逻辑(value为隐式参数)
}
访问器类型对比[编辑 | 编辑源代码]
访问器类型 | 功能 | 是否必须 |
---|---|---|
get | 返回值 | 可选(只写属性需标记为set-only) |
set | 赋值操作 | 可选(只读属性需标记为get-only) |
基础示例[编辑 | 编辑源代码]
以下示例展示包含完整访问器的属性:
private string _name; // 私有字段(backing field)
public string Name
{
get { return _name; }
set { _name = value; } // value是编译器自动生成的上下文关键字
}
使用效果:
var obj = new MyClass();
obj.Name = "Alice"; // 调用set访问器
Console.WriteLine(obj.Name); // 调用get访问器,输出"Alice"
高级用法[编辑 | 编辑源代码]
自动实现属性[编辑 | 编辑源代码]
C# 3.0+支持简写语法(编译器自动生成隐藏字段):
public int Age { get; set; } // 自动生成私有字段
访问修饰符控制[编辑 | 编辑源代码]
可对访问器单独设置可见性:
public string ID { get; private set; } // 外部只读,类内可写
表达式体属性[编辑 | 编辑源代码]
C# 6.0+支持Lambda风格简写:
public string Greeting => $"Hello, {Name}"; // 只读属性
初始化器语法[编辑 | 编辑源代码]
C# 6.0+允许属性初始化:
public DateTime Created { get; } = DateTime.Now; // 只读初始化
验证逻辑示例[编辑 | 编辑源代码]
属性访问器的核心价值在于添加业务逻辑:
private int _score;
public int Score
{
get => _score;
set
{
if (value < 0 || value > 100)
throw new ArgumentOutOfRangeException("分数必须在0-100之间");
_score = value;
}
}
性能考量[编辑 | 编辑源代码]
属性访问器会被JIT编译器内联优化(inline),其性能与直接字段访问几乎相同。但包含复杂逻辑的访问器会失去内联机会。
设计模式应用[编辑 | 编辑源代码]
观察者模式实现[编辑 | 编辑源代码]
通过属性触发状态变更通知:
public class Subject
{
private string _state;
public string State
{
get => _state;
set
{
_state = value;
NotifyObservers(); // 值变更时自动通知观察者
}
}
}
延迟加载模式[编辑 | 编辑源代码]
private ExpensiveObject _expensive;
public ExpensiveObject Expensive
{
get
{
if (_expensive == null)
_expensive = InitializeExpensiveObject();
return _expensive;
}
}
编译器处理机制[编辑 | 编辑源代码]
属性访问器会被编译为IL代码中的特殊方法:
实际生成的IL方法名为:
- get_PropertyName
- set_PropertyName
最佳实践[编辑 | 编辑源代码]
1. 优先使用自动属性简化代码 2. 需要验证/转换逻辑时使用完整属性 3. 集合属性应返回副本或只读集合 4. 避免在访问器中执行耗时操作 5. 线程敏感场景需添加同步锁
常见问题[编辑 | 编辑源代码]
为什么不用公共字段?[编辑 | 编辑源代码]
- 无法后续添加验证逻辑
- 不支持数据绑定
- 不利于版本控制(字段改为属性会导致二进制不兼容)
循环调用问题[编辑 | 编辑源代码]
错误示范:
public string BadExample
{
get { return BadExample; } // 栈溢出!
set { BadExample = value; } // 无限递归
}
正确做法应始终使用独立的支持字段(backing field)。