Effective C# Item1:Always Use Properties Instead of Accessible Data Members
在C#语言中,属性的地位已经从编程习惯提升至语言的特性(这里用到first-class language。这个含义可以参见wikipedia)。你不必为你的类型创建公有数据成员,也不必手工创建属性中的get和set方法。属性让你可以通过公有接口暴露数据成员,同时又可以提供oo环境中你所希望的封装。属性在可达性上就像一个公有数据成员,但是实现上像一个方法。
对于一些类型的成员来讲,例如客户的姓名、xy坐标等,数据型是它们最好的表现形式。属性允许你创建一个接口,使得对它们的操作可以像公有数据成员一样,同时又保留了使用成员函数一样的优势。
在.net framework中已经假设你会为你的公有数据成员使用属性。实际上,在.net framework的数据绑定中支持的也是属性而不是公有的数据成员。不论是web控件还是win form控件,都是通过属性来实现数据绑定的。下面的例子中,数据绑定机制通过反射在已知类型中寻找特定属性:

上面的代码将adress中的City属性绑定到名为textBoxCity控件的Text属性上。如果City不是属性而是adress中的一个公有成员,上面的代码就不能实现。
当遇到新的需求或行为变更时,属性更容易修改。例如你现在决定客户的名字不能为空,那你只要在对应属性的set中添加判断就可以了。



















但是如果你使用的是公有数据成员,那你必须检查你所有的代码并一一添加判断,那将消耗大量的时间。
属性对于多线程的操作支持也很方便。一般的只要在get和set时加锁就可以了。





















属性拥有方法的语言特性。属性可以为虚拟成员(virtual)
















同样属性可以是抽象的(abstract)或是接口定义中的一部分







我们还可以通过接口属性来创建常量














在C#2.0中可以对get和set设定不同的可达性,这使得程序可以更加可控。下例中对Name属性的set为protected
















属性不仅可以应用在简单数据类型领域。你可以使用索引来创建返回序列项的属性:















索引拥有所有简单属性所包含的特性,可以在索引内部进行校验和计算,可以为虚拟或抽象成员,可以在接口中声明,可以设为只读或读写。一维整型参数的索引可以参与数据绑定,其他非整型参数索引可以定义映射(map)和字典(dictionary)。
C#中你可以创建多维索引如下例所示:



























注意所有的索引在声明时都使用this关键字。索引不能自定义命名。在一个类型中,同样参数的索引最多只能有一个。
虽然属性有很多好处,但是有人会依旧先创建公有成员,当需要用到属性的特性时在将其修改为属性。这看起来好像是蛮有道理的,不过这是不好的做法。对于属性和公有成员来说,它们的实现代码是一致的,这会为我们造成误解,认为只要修改公有成员为属性就万事大吉了。实际上这种说法只是部分的正确。
在C#的源码中使用属性和公有成员的代码虽然相同,但通过编译器生成的中间语言却是不同的。它们是代码兼容的,重新编译就可以正确运行,但却不是二进制兼容的。(对于二进制兼容这点,我也不是十分清楚… 我觉得这种兼容是应该与开发平台无关的,因此上面的改变才会引发不兼容性。望指教)。
在效率上虽然使用属性并不会比使用公有数据成员快,但是一般情况下也不会比它慢。属性在JIT编译器编译时做为内联函数(inline)处理,相当程度上减少了效率的损失。
P.S. Item1还是比较简单的,就不写什么了。在我刚参加工作的时候就在老一辈的指导下养成了这个习惯,虽然声明时候会麻烦一些,但是比起事后的修改工作实在是好太多了… 另外可将其在不同权限下分别设为只读或读写非常实用。
回到目录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通