当前位置: 首页 > news >正文

【原理】C# 字段、属性对比及其底层实现

【从UnityURP开始探索游戏渲染】专栏-直达

在C#中,‌字段Field‌和‌属性Property‌是类中用于存储和访问数据的两种成员,它们的核心区别如下:


‌定义与语法‌

字段

  • 直接声明在类中的变量,用于存储数据,通常为私有(private)以封装内部状态。
csharp
private string _name;// 字段(惯例:下划线前缀命名)public int Age;// 公共字段(不推荐直接暴露)

属性

  • 通过get/set访问器封装字段,控制外部访问逻辑。支持自动属性(编译器生成隐藏字段)。
csharp
public string Name { get; set; }// 自动属性public int Score {get => _score;set { if (value >= 0) _score = value; }// 带验证的手动属性
}

‌核心区别‌

特性字段属性
数据封装直接暴露数据,无逻辑控制通过访问器封装,支持验证、计算等逻辑
访问控制通常设为private可独立设置get/set的访问权限(如private set
性能直接内存操作,无额外开销轻微方法调用开销(JIT可能内联优化)
数据绑定支持不支持(如WPF绑定)支持,需实现INotifyPropertyChanged
线程安全需手动同步可在访问器中添加lock等同步机制
扩展性仅存储数据支持计算属性、惰性加载等高级特性

‌设计原则与使用场景‌

  • 优先使用属性‌公共数据成员应通过属性暴露,遵循封装原则,便于后续扩展逻辑(如数据验证、通知机制)。
  • 字段适用场景‌类内部临时变量、readonly常量或高性能敏感场景(如游戏开发)。
  • 自动属性的限制‌自动属性({ get; set; })适用于简单数据存储,若需逻辑控制需转为手动实现。

‌底层实现差异‌

  • 属性本质是编译器生成的get_XXXset_XXX方法,IL代码中标记为property元数据。
  • 反射时,字段通过Type.GetFields()获取,属性通过Type.GetProperties()获取。

‌‌小结:属性是面向对象设计中封装的核心手段,而字段侧重内部数据存储。实际开发中应优先使用属性,仅在特定场景(如性能优化)选择字段

C# 属性的实现原理

属性本质上是编译器对‌访问器方法‌和‌私有字段‌的语法糖封装,其核心机制可分为以下层次:


一、底层编译原理

自动属性的字段生成

当声明 public int Value { get; set; } 时,编译器会自动生成一个名为 <<Value>k__BackingField> 的私有字段,并生成对应的 get_Value() 和 set_Value(int value) 方法。

csharp
// 编译后等效代码private int <<Value>k__BackingField>;
public int get_Value() => <<Value>k__BackingField>;
public void set_Value(int value) => <<Value>k__BackingField> = value;

手动属性的显式控制

若显式定义私有字段和访问器(如 private int _value;),编译器直接生成与属性同名的方法(IL 层面仍为 get_XXX/set_XXX)。


二、访问器方法特性

get 访问器

  • 编译为返回字段值的方法,无参数。
  • 若包含逻辑(如 return _value * 2;),编译器会将逻辑嵌入方法体。

set 访问器

  • 编译为接受 value 参数(类型与属性一致)的 void 方法。
  • 支持数据验证(如 if (value > 0) _value = value;),验证逻辑会被编译为条件跳转指令。

三、元数据与反射

属性标记为特殊成员

在 IL 代码中,属性被标记为 property 元数据,并通过 .method 指令关联到生成的 get/set 方法。

il
.property instance int32 Value() {.get instance int32 MyClass::get_Value().set instance void MyClass::set_Value(int32)
}

反射可区分属性与字段

通过 Type.GetProperties() 获取属性列表,而字段需通过 GetFields(),二者在元数据层完全独立。


四、性能优化

JIT 内联优化

简单属性(如自动属性)的 get/set 方法通常会被 JIT 编译器内联,最终执行效率与直接访问字段几乎相同。

动态代理限制

因属性本质是方法,动态代理(如 DynamicObject)需重写 TryGetMember/TrySetMember 来模拟属性行为.


五、高级特性扩展

init 访问器

C# 9.0 引入的 init 访问器编译为 modreq 修饰的方法,确保属性仅在对象初始化阶段可赋值。

表达式体属性

public int Value => _value; 会被编译为仅包含 get 方法的简化属性。


通过上述机制,C# 属性在语法层面实现了字段式的简洁访问,同时在底层维护了面向对象的数据封装原则


【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

http://www.dtcms.com/a/334591.html

相关文章:

  • 手机版碰一碰发视频系统批量剪辑功能开发,支持OEM贴牌
  • 编写和运行 Playbook
  • 31 HTB Union 机器 - 中等难度
  • Java设计模式之《工厂模式》
  • DAY12DAY13-新世纪DL(Deeplearning/深度学习)战士:破(改善神经网络)1
  • 嵌入式硬件篇---常见的单片机型号
  • ​进程与线程(线程)
  • 【JavaEE】多线程 -- 线程等待wait和notify
  • 对话访谈|盘古信息×智晟威:深度挖掘数字化转型的奥秘
  • 【数据结构】深入理解单链表与通讯录项目实现
  • git revert
  • Java Condition 对象 wait 方法使用与修复方案
  • 云计算-Kubernetes+Istio 实现金丝雀发布:流量管理、熔断、流量镜像、ingreess、污点及pv案例实战
  • 如何防止 RabbitMQ 的消息丢失?如何保证消息的可靠传输?
  • Python 项目高频设计模式实战指南:从理念到落地的全景剖析
  • Linux软件编程--线程
  • 蓝牙音频ANC四通道测试解决方案
  • 新经济形态下人才能力结构变革与发展研究报告
  • Win10快速安装.NET3.5
  • RecSys:多目标模型和MMOE
  • .NET8下的Garnet使用
  • androidstudio内存大小配置
  • 研究生第十六周周报
  • 718SJBH公交查询系统
  • 4.6 Vue 3 中的模板引用 (Template Refs)
  • Sklearn 机器学习 邮件文本分类 加载邮件数据
  • [Responsive theme color] 动态主题 | 色彩工具函数 | HEX与RGB
  • 嵌入式第三十天(线程)
  • 围棋对战游戏开发详解 附源码
  • 【C++】标准库中用于组合多个值的数据结构pair、tuple、array...