C# 的属性和索引的封装的意义
这个问题得从两个层面回答,封装和方法本身。先来说一下封装。
属性就是用来包装字段、为字段添加额外处理逻辑和封装逻辑的。
举个例子,你的鼻子和嘴巴都能呼吸,但是嘴还能吃饭,那么嘴的功能相较于鼻子更完备,那干脆都用嘴巴呼吸算了,何必用鼻子呢?实际上鼻子封装了呼吸的逻辑,保证吸入空气里的灰尘能降到最小,这就是封装给我们带来的用途。如果我们全凭靠嘴巴呼吸的话,确实鼻子没有用,但这对我们人的身体会带来潜在的危险,诸如吸入灰尘沾到肺上之类的。
属性和索引器就是这样的一种存在,专门封装字段的处理逻辑,以防别的程序员调用时乱使用字段,给字段处理加上任意执行内容,以至于出现危险的情况。
和
显然我们就能看出区别。没有属性,我们就可以给年龄赋值为任意数值,甚至负数也没问题。万一有些时候处理逻辑里用到 数值的时候出现了各种奇葩结果,那你这个时候再来反推出是传参的 bug,我估计就比较麻烦了。
索引器也是一样的道理,不过索引器侧重于集合对象使用。如果一个对象专门用来存储一系列元素,那么这种对象就有可能需要索引器的支持。和前文的思维一样,如果你没有索引器进行索引的验证,那么赋的值就可能更加随意,使得出现意外情况。
实际上还有一个封装的东西:事件。事件是委托字段的封装,委托字段用于执行内部对象的特定时刻触发的行为。当我们直接把对象里的委托字段改为 时,别人就可以给委托字段赋值为任意同样委托类型的执行操作,使得行为变更,相当于“截胡”。
事件的存在就是为了解决封装。事件只允许我们可以对对象添加和删除指定的处理逻辑。而内部的委托字段不允许我们从外部直接使用它们。这样的话,我们从外部调用对象的话,就只可以为对象添加新的处理逻辑,或者移除掉添加过的处理逻辑了。这样比起原本直接访问委托字段,为其设置处理逻辑算是迂回了一下,但起到封装的机制,防止我们篡改原定的逻辑。
所以:建议书写格式为:
或者用高级的语法,省略 和
块。
实际上这两种写法有一点不同,你可以参看 Sharplab.io 网站的生成代码。后者处理的内容会更多一些。
所以请记住,属性、索引器和事件专门为普通字段和委托字段提供封装。这是侧重封装层面解答的第一个问题。
第二个问题,方法。方法确实可以解决所有属性、索引器的赋值行为,因为只要我们有了方法,就可以书写类似于 和
的方法,这样依然和属性具有等同效果。之所以 C# 出了一个属性机制(还有索引器),实际上是为了简化书写代码和规范化处理逻辑,将类似于
这种代码改写为
更为直观和更具有可读性的赋值语句形式。索引器也是一样,有些时候用
方法获取一个集合对象内部的某一个索引位置的元素时,用索引器写起来的样子比起方法调用形式要更具可读性(尽管你看方法看习惯了,但是带有看起来像是数学符号的这些操作符,总能让我们更清晰一些,只要这些操作符得是我们常见的符号)。
顺带说一下, 这个语句是给
属性赋值为 20,并非直接操作了
字段。在这个等号的赋值行为中,我们可以加入类似于前面的数据验证,如果超过范围就产生异常,于是整个等号的意义就发生了变化(它不仅仅有赋值行为,还有验证行为),所以这也是一个重要的知识点。