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开发和热衷于开源高性能系统设计,通过博文交流和分享经验,欢迎转载,请保留原文地址,谢谢。 |