C# 属性(Property)(一文讲透)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
- 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在 C# 开发中,属性(Property) 是面向对象编程的核心概念之一。它不仅是数据封装的桥梁,更是代码规范性与可维护性的关键。对于编程初学者而言,理解属性的定义、作用及使用场景,能够帮助快速掌握面向对象的设计思想;对于中级开发者,深入探索属性的高级特性(如延迟加载、索引器等),则能显著提升代码的优雅度与复用性。本文将从基础到进阶,结合实例与比喻,系统性地讲解 C# 属性的方方面面。
一、属性的基础概念与核心作用
1.1 什么是属性?
属性(Property) 是一种用于访问对象状态的特殊成员,它通过 get
和 set
访问器控制对字段(Field)的读写操作。简单来说,属性是“包装”字段的接口,对外隐藏了字段的具体实现细节。
比喻:
可以将属性想象为银行保险箱的“访问接口”。客户(调用者)通过属性(如 GetBalance()
或 SetBalance()
)操作账户金额,而无需直接接触保险箱(字段)的物理结构。
public class BankAccount
{
private decimal _balance; // 字段(隐藏的)
public decimal Balance // 属性
{
get { return _balance; }
set { _balance = value; }
}
}
1.2 属性的核心作用
- 数据封装:通过属性访问字段,避免外部直接修改字段值,确保数据的一致性。
- 逻辑控制:在
get
或set
中添加验证、计算或日志记录等逻辑。 - 接口统一:即使字段的存储方式(如从数据库或内存中获取)发生变化,属性的外部接口保持不变。
二、属性的访问器(Accessor)详解
2.1 get
和 set
访问器
每个属性可以包含 get
和 set
两个访问器,分别用于获取和设置属性值。
示例:基础属性
public class Student
{
private string _name; // 私有字段
public string Name // 公共属性
{
get { return _name; } // 获取值
set { _name = value; } // 设置值,value 是隐含的临时变量
}
}
简写形式(自动属性)
C# 允许通过自动属性简化代码:
public string Name { get; set; } // 等价于上述代码
2.2 只读属性与只写属性
- 只读属性:仅包含
get
访问器,常用于返回计算值或不可修改的数据。 - 只写属性:仅包含
set
访问器,但实际开发中较少使用。
示例:只读属性
public class Rectangle
{
private int _width;
private int _height;
public int Area // 只读属性
{
get { return _width * _height; }
}
}
比喻:
只读属性就像超市的“只读价目表”,顾客可以看到价格,但无法直接修改。
三、属性的高级特性与应用场景
3.1 延迟加载(Lazy Loading)
通过 get
访问器实现延迟初始化,仅在首次访问时加载数据,提升性能。
示例:延迟加载图片资源
public class ImageLoader
{
private Bitmap _image;
private readonly string _filePath;
public ImageLoader(string path)
{
_filePath = path;
}
public Bitmap Image // 属性实现延迟加载
{
get
{
if (_image == null)
_image = LoadImageFromDisk(_filePath);
return _image;
}
}
private Bitmap LoadImageFromDisk(string path)
{
// 模拟耗时的磁盘读取操作
return new Bitmap(path);
}
}
3.2 索引器(Indexer)
索引器允许像数组一样通过索引访问对象的成员,本质是一种特殊类型的属性。
示例:自定义集合类的索引器
public class MyCollection<T>
{
private List<T> _items = new List<T>();
public T this[int index] // 索引器属性
{
get { return _items[index]; }
set { _items[index] = value; }
}
}
3.3 表达式主体属性
C# 6.0 引入了表达式主体属性(Expression-Bodied Properties),进一步简化代码。
public class Circle
{
private double _radius;
public double Area => Math.PI * _radius * _radius; // 等价于只读属性
}
四、最佳实践与注意事项
4.1 属性的命名规范
- 遵循 PascalCase 命名规则(如
CustomerName
)。 - 避免与字段名称重复(如字段
_balance
对应属性Balance
)。
4.2 避免副作用
在 set
访问器中应仅执行与属性值相关的操作,避免引发复杂逻辑(如网络请求或数据库操作)。
错误示例:
public class InvalidExample
{
public int Count
{
set
{
// 不好的做法:设置 Count 同时触发网络请求
_count = value;
SendCountToServer();
}
}
}
4.3 使用特性(Attribute)增强属性
通过自定义特性(如 [Required]
)为属性添加元数据,常用于数据验证或序列化场景。
public class User
{
[Required(ErrorMessage = "姓名不能为空")]
public string Name { get; set; }
}
五、实战案例:综合应用属性
案例 1:学生信息管理系统
public class Student
{
public string Name { get; set; }
private int _age;
public int Age
{
get => _age;
set
{
if (value < 0 || value > 150)
throw new ArgumentOutOfRangeException("年龄应在 0-150 之间");
_age = value;
}
}
}
案例 2:银行账户的余额验证
public class BankAccount
{
private decimal _balance;
public decimal Balance
{
get => _balance;
set
{
if (value < 0)
throw new InvalidOperationException("余额不能为负数");
_balance = value;
}
}
}
六、属性在框架与设计模式中的应用
6.1 在 ASP.NET Core 中的 Model Binding
控制器的参数通过属性名与 HTTP 请求的键自动绑定,依赖属性的命名一致性。
6.2 在 MVVM 模式中的数据绑定
WPF 或 UWP 中,ViewModel 的属性通过 INotifyPropertyChanged
接口实现动态更新。
public class PersonViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
结论
C# 属性(Property) 是构建健壮、可维护代码的核心工具。通过封装字段、控制访问逻辑、实现延迟加载或索引器等高级特性,开发者能够显著提升代码的规范性与扩展性。无论是基础的 get
/set
访问器,还是结合设计模式的复杂场景,属性始终扮演着“数据守护者”的角色。建议读者在实践中多尝试通过属性实现业务逻辑的解耦,并结合特性、索引器等进阶功能,逐步掌握 C# 属性的精髓。