十、属性(Properties)
CLR #属性
📚 第十章:Properties(属性)
现在我将重新,严格按照高阶标准来总结这章内容,包括:
- 深度提炼原理和设计动机
- 源码层次的理解(IL / CLR层面)
- Mermaid 图 + 丰富代码示例
- 至少5道高阶面试题 + 精确解析
立刻开始,稍等⏳!
📚《CLR via C#》第十章 高阶总结
✨ 深度版总结:遵循你的要求(二八定律提炼 + 深度原理 + Mermaid图示 + 面试题 ≥5道)
✅ 第十章:Properties(属性)
1. 📌 什么是属性(Properties)
- 属性(Property)本质是方法(getter/setter)的语法糖(Syntax Sugar)。
- 在 CLR 元数据中,属性是通过
PropertyDef表示,但并没有直接存储值。 - 属性通过访问器方法实现:
get_PropertyName()set_PropertyName(Type value)
🚀 底层示意(IL):
public int Age { get; set; }
对应的 IL:
.method public hidebysig specialname instance int32 get_Age() { ... }
.method public hidebysig specialname instance void set_Age(int32 'value') { ... }
✅ 注意:访问器方法都标记了 specialname,意味着编译器/工具会特殊对待。
2. 📌 自动实现属性(Auto-Implemented Properties)
- 让编译器自动生成私有字段,减少样板代码。
- 特点:
- 快速定义简单的属性
- 后期可以通过 "refactor" 手动扩展 get/set 逻辑
public string Name { get; set; }
实际上:
private string <Name>k__BackingField;
public string Name {
get { return <Name>k__BackingField; }
set { <Name>k__BackingField = value; }
}
3. 📌 只读属性(Read-Only Properties)
- 仅提供
get,没有set,使对象具备不可变性(Immutability)特征。 - 可与 构造函数初始化配合使用。
public string Id { get; }
public MyClass(string id) => Id = id;
4. 📌 属性与字段的根本区别
| 比较项 | 属性(Property) | 字段(Field) |
|---|---|---|
| 编译后的表示 | 方法(get_/set_) | 内存块 |
| 可控制访问 | 支持(如只读、延迟加载) | 无 |
| 兼容性 | 支持版本兼容 | 直接暴露,不安全 |
| 特性附加 | 支持加特性 [Attribute] |
支持 |
| 性能 | JIT可能内联简单属性 | 直接访问 |
✅ 结论:设计类时推荐优先使用属性,除非需要暴露不可见性。
5. 📌 索引器(Indexer)
- 本质是带参数的属性,允许对象像数组一样通过
[]下标访问。
public string this[int index] {
get { return elements[index]; }
set { elements[index] = value; }
}
IL对应方法名:
get_Item(int)set_Item(int, value)
基础示例:封装数组
public class WeekDays
{
private string[] days = new string[7];
// 定义索引器
public string this[int index]
{
get
{
if (index < 0 || index >= days.Length)
throw new IndexOutOfRangeException("Invalid day index.");
return days[index];
}
set
{
if (index < 0 || index >= days.Length)
throw new IndexOutOfRangeException("Invalid day index.");
days[index] = value;
}
}
public int Length => days.Length;
}
调用:
var week = new WeekDays();
week[0] = "Sunday";
week[1] = "Monday";
Console.WriteLine(week[0]); // 输出 Sunday
Console.WriteLine(week[1]); // 输出 Monday
6. 📌 可访问性控制(Access Modifiers)
get和set可以分别设置不同的访问级别。
public string Name { get; protected set; }
- 应用场景:
- 外部只能读,子类可写
- 设计更精细化的 API
7. 📌 属性设计原则(Architectural Best Practices)
✅ 属性应该:
- 读取快速,无副作用(不能启动复杂逻辑)
- 计算量小(避免I/O,数据库访问)
- 不抛出异常(除非特殊设计)
✅ 不应该:
- 使属性
get方法执行复杂任务 - 导致长时间阻塞
- 改变系统状态(属性访问应该是幂等(安全可重复执行)的)
8. 📌 Mermaid 图示:属性本质结构
flowchart TD
A[Property Name] --> B[get_Property Name]
A --> C[set_Property Namevalue]
B --> D[返回内部字段]
C --> E[更新内部字段]
🧠 面试题
1️⃣ 属性和字段在CLR内部有什么根本区别?
✅ 答:
- 字段是数据,属性是方法;
- 属性通过
get/set方法暴露,不占直接数据空间; - 字段直接映射内存,属性可隐藏实现细节,保证兼容性。
2️⃣ 为什么属性的访问器要用 specialname 标记?
✅ 答:
specialname告诉 IL 工具和 CLR,get_X/set_X是属性访问器;- 允许编译器和反射 API 自动关联到属性,而不是普通方法。
3️⃣ 自动属性的Backing Field如何命名?
✅ 答:
- 由编译器生成,通常是
<PropertyName>k__BackingField; - 属于 Compiler Generated Members,不应该直接访问。
4️⃣ 索引器与普通属性在 IL 层面的主要区别是什么?
✅ 答:
- 索引器通过
get_Item/set_Item命名; - 支持带参数的访问,而普通属性无参数。
5️⃣ 为什么属性访问应该是无副作用且快速的?
✅ 答:
- 属性语义上是轻量访问(像字段一样)
- 调试器/序列化工具会无意中触发
get,如果有副作用会破坏系统一致性。
🧹 总结表格
| 概念 | 要点 |
|---|---|
| 属性本质 | 访问器方法(get/set)的语法糖 |
| 属性 vs 字段 | 方法 vs 内存块 |
| 自动属性 | 编译器生成 Backing Field |
| 只读属性 | 只提供 get,不可更改 |
| 索引器 | 参数化属性 |
| 设计原则 | 快速,无副作用,幂等 |
点赞鼓励下,(づ ̄3 ̄)づ╭❤~
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号