Effective C#读书笔记(1)使用属性Property,不要使用公有数据成员

来源:互联网 发布:windows.h 下载 编辑:程序博客网 时间:2024/06/03 20:46
  
        C# 的Property是其十分重要的语言特性,我们用在类中再创建公有变量,也不必再手动编写get和set方法了。通过Property来访问数据成员,表面上看,通过Property来设置或获取字段值和直接把这个字段暴露为公有似乎没有什么区别,但是,他们的后台实现却大相径庭,因为Property实际上是通过方法(成语函数)来实现的。比如我们这样设计一个类:
// using public data members, bad practice:
public class Customer
{
public string Name;
 
// remaining implementation omitted
}
//…
string name = customerOne.Name;//读取字段值
customerOne.Name = "This Company, Inc.";//设置字段值
使用.Net自带的ILDASM工具,可以看到编译成的中间代码如下:
.field public string Name
访问字段值——
ldloc.0
ldfld      string NameSpace.Customer::Name
stloc.1
设置字段值——
ldloc.0
ldstr      "This Company, Inc."
stfld      string NameSpace.Customer::Name
 
如果使用Property,则类的部分代码如下:
public class Customer
{
 private string _name;
 public string Name
 {
    get
    {
      return _name;
    }
    set
    {
      _name = value;
    }
 }
 // remaining implementation omitted
}
//访问属性的代码和访问字段代码看起来一样
string name = customerOne.Name;
customerOne.Name = "This Company, Inc.";
但此时产生的IL代码如下:
.property instance string Name()
{
 .get instance string NameSpace.Customer::get_Name()
 .set instance void NameSpace.Customer::set_Name(string)
} // end of property Customer::Name
 
.method public hidebysig specialname instance string
        get_Name() cil managed
{
 // Code size       11 (0xb)
 .maxstack 1
 .locals init ([0] string CS$00000003$00000000)
 IL_0000: ldarg.0
 IL_0001: ldfld      string NameSpace.Customer::_name
 IL_0006: stloc.0
 IL_0007: br.s       IL_0009
 IL_0009: ldloc.0
 IL_000a: ret
} // end of method Customer::get_Name
.method public hidebysig specialname instance void
        set_Name(string 'value') cil managed
{
 // Code size       8 (0x8)
 .maxstack 2
 IL_0000: ldarg.0
 IL_0001: ldarg.1
 IL_0002: stfld      string NameSpace.Customer::_name
IL_0007: ret
} // end of method Customer::set_Name
 
// get
ldloc.0
callvirt   instance string NameSpace.Customer::get_Name()
stloc.1
 
// set
ldloc.0
ldstr      "This Company, Inc."
callvirt   instance void NameSpace.Customer::set_Name(string)
在C# 的代码自动完成功能中,我们也可以看到两个Name前面的小标记是不一样的,是两个看上去相貌相似而完全不同的“人”。所以如果更改类中的公有Name字段为属性Name和私有成员_name,客户代码需要重新编写和编译。
       使用Property有很多好处——
       1)数据绑定支持属性而非公有成员。
       把address对象的City属性绑定到文本框textBoxCity的Text属性可以这样写:
       textBoxCity.DataBindings.Add( "Text", address, "City" );
       此处如果Text不是一个Property而是一个public field,运行是将会出现错误。
       2)使用属性可以对输入进行验证。
       因为属性本质上是通过方法实现的,所以在方法中,我们可以在赋值发生前加入自己的验证代码,由类的设计者来确保输入的正确。比如Customer类的Name不可为空:
public class Customer
{
 private string _name;
 public string Name
 {
    get
    {
      return _name;
   }
    set
    {
      if (( value == null ) ||
        ( value.Length == 0 ))
        throw new ArgumentException( "Name cannot be blank",
          "Name" );
      _name = value;
    }
 }
 
 // ...
}
       3)属性对多线程程序的支持更好。
       还是因为属性是基于方法的,为了防止线程冲突,可以这样编写属性:
public string Name
{
 get
 {
    lock( this )
    {
      return _name;
    }
 }
 set
 {
    lock( this )
    {
      _name = value;
    }
 }
}
       4)属性可以是“虚”的。
       我们在接口或基类中定义属性,在派生类中可以像方法一样override它。虚函数的好处就不用说了,了解面向对象的地球人都知道。
       5)属性和索引器。
       C# 中类的索引器是一种特殊的带参数的属性。
public int this [ int index ]
{
 get
 {
    return _theValues [ index ] ;
 }
 set
 {
    _theValues[ index ] = value;
 }
}
 
// Accessing an indexer:
int val = MyObject[ i ];
       MSDN上说,索引器允许类或结构的实例按照与数组相同的方式进行索引,用起来还是满方便的。
原创粉丝点击