Effective C# 条款1
属性的优点:
-
遵循面向对象的原则
-
具有弹性
1. 1 遵循面向对象的原则
属性在使我们可以将数据成员暴露为公有接口的同时,还为我们提供了在面向对象环境中所期望的封装。从表面看起来像是数据成员,但内部却是以方法实现。
public class TestClass
{
//成员变量
public string Name;
}
public class TestClass
{
//属性
public string Name { get; set; }
}
TestClass testClass = new TestClass();
testClass.Name = "TestClass";
这段代码非常简洁和直观。有人据此就认为以后如果有需要,再将 TestClass 类的数据成员 Name 替换为属性OK了,而使用TestClass类型的代码无需做任何改变。这种说法从某种程度上来讲是对的。
属性在被访问的时候和数据成员看起来没有什么差别。这正是 C#引入新的属性语法的一个目标。但属性毕竟不是数据,访问属性和访问数据产生的是不同的 MSIL。在程序编译后,编译器在把程序编译成为MSIL时,会自动把属性中的get/set编译成为两个方法,所以属性能够获得函数的全部好处。但实际区别我们可以通过如下的MSIL分析可知成员变量和属性是存在区别的。
图1 TestClass类编译后的MSIL
通过图1我们没有找到Name属性,取而代之的是两个get_Name / set_Name方法,充分说明了属性是通过方法的方式调用。
图2成员变量MSIL
图3属性MSIL
大家注意尽管访问属性和访问数据成员使用的是同样的 C#源代码,但是 C#编译器却将它们转换为不同的IL代码。
换句话说,虽然属性和数据成员在源代码层次上是兼容的,但是在二进制层次上却不兼容。这意味着如果将一个类型的公有数据成员改为公有属性,那么我们必须重新编译所有使用该公有数据成员的C#代码。
1.2 具有弹性
使用属性在修改上也比成员变量更具有弹性,例如程序要判断成员变量是否为空时,我们就需在代码某处加上判断逻辑,而属性我们只需在属性上加上判断逻辑,省去了烦心查找要加逻辑的地方。
private string _name;
public string Name
{
get
{
if (_name == null)
return string.Empty;
return _name;
}
set
{
_name = value;
}
}
由于属性是采用方法来实现的,因此为它们添加多线程支持就更加容易——直接在get和 set方法中提供同步数据访问控制即可:
public string Name
{
get
{
lock( this )
{
return _name;
}
}
set
{
lock( this )
{
_name = value;
}
}
}
经验总结:
只要打算将数据暴露在类型的公有接口或者受保护接口中,我们都应该使用属性来实现。对于具有序列或者字典特征的类型,则应该采用索引器。
![]() |
关于作者:[作者]:
JK_Rush从事.NET开发和热衷于开源高性能系统设计,通过博文交流和分享经验,欢迎转载,请保留原文地址,谢谢。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架