C#基础 [09] 属性

一、属性的定义和特征

  1.属性的定义

    属性是表示类或类的实例中的一个数据项的成员:它提供灵活的机制来读取、编写或计算某个私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。这使得可以轻松访问数据,此外还有助于提高方法的安全性和灵活性。

  2.属性的特征

    1).属性是命名的类成员,它有类型,可以被赋值和读取。

    2).与字段不同,属性是一个函数成员;它本身没有任何存储,不为数据存储分配内存;它执行代码。

    3).属性有两个匹配的、称为访问器的方法:set访问器为属性赋值,get访问器从属性中获取值。

    4).在使用属性时,属性访问器被隐式调用:当为属性赋值时,set访问器被隐式调用;当从属性中读取值时,get访问器被隐式调用;不能显式调用访问器。

  3.属性的示例   

View Code
 1     class TimePeriod
 2     {
 3         // 声明一个后备字段
 4         private double seconds;
 5 
 6         // 声明一个doub类型的属性,在属性内部将小时转换为秒
 7         public double Hours
 8         {
 9             // get访问器:返回属性类型的值
10             get { return seconds / 3600; }
11             // set访问器:为属性赋值
12             set { seconds = value * 3600; }
13         }
14     }    
15 
16     class Program
17     {
18         static void Main(string[] args)
19         {
20             TimePeriod t = new TimePeriod();
21 
22             // 为属性赋值,隐式调用set访问器.
23             t.Hours = 24;
24 
25             // 读取属性值,隐式调用get访问器.
26             System.Console.WriteLine("Time in hours: " + t.Hours);
27         }
28     }

二、属性访问器

  属性的访问器就是两个方法,set访问器和get访问器有预定义的语法和语义。我们按照预定义的语法声明的属性,经过编译器编译之后,会自动分别为set访问器和get访问器生成相应的方法。编译器生成的方法经过反编译可以看得一清二楚。下面是我用Reflector反编译上例中的TimePeriod类之后,它的Hours属性的结构:

  可以看到,编译器为Hours属性生成了一个返回值类型为void的方法名为set_Hours的方法,一个返回值类型为double的get_Hours方法。

  1.set访问器

    从上图已经可以看到了属性内部的结构。这里再来进一步分析属性所生成的setHours方法内部的秘密。经过反编译之后的set_Hours方法的内部代码如下:

1 public void set_Hours(double value)
2 {
3     this.seconds = value * 3600.0;
4 }

    从上面代码中我们可以分析得出set访问器的特征:

      1).set访问器有一个单独的、隐式的值参数,参数名称为value,参数类型与属性的类型相同。

      2).set访问器的返回类型为void。

  2.get访问器

    下面是get_Hours方法的内部实现代码:

1 public double get_Hours()
2 {
3     return (this.seconds / 3600.0);
4 }

    get访问器的特征:

      1).get访问器没有参数。

      2).get访问器有一个与属性类型一致的返回值类型。

      3).get访问器的所有执行路径必须有一条return语句,返回属性类型的值。

三、属性和关联字段

  1.属性通常和字段关联,字段存储实际的数据,而属性为字段提供了一种访问渠道。通常的做法是:在类中将字段声明为private,并且为字段声明一个public属性以提供受控的从类的外部对字段的访问。和属性关联的字段称为“后备字段"。

  2.属性和后备字段有两种命名约定:

    第一种:后备字段采用camel命名法,而属性采用字段的Pascal命名版本。

    第二种:属性命名不变,仍然使用Pascal命名法,字段采用"下划线+camel"的形式。

   3.下面是另一个示例:该示例声明了一个名为Student的类,该类的每个属性都有一个采用了“_+camel”命名的后备字段。其中类成员声明按照:Fields—>Methods->Properties的顺序进行。

View Code
 1 using System;
 2 namespace MyClass
 3 {
 4     class Student
 5     {
 6         // Fields
 7         private string _name;
 8         private string _sex;
 9         private int _age;
10         private float _chinese;
11         private float _math;
12         private float _english;
13 
14         // Constructor
15         public Student()
16         {
17 
18         }
19 
20         // Methods
21         public void SayHello()
22         {
23             System.Console.WriteLine("我叫{0},今年{1}岁了,是{2}同学", this.Name, this.Age, this.Sex);
24         }
25         
26         /// <summary>
27         /// 总成绩
28         /// </summary>
29         /// <returns></returns>
30         public float Sum()
31         {
32             return this.Chinese + this.Math + this.English;
33         }
34 
35         /// <summary>
36         /// 平均成绩
37         /// </summary>
38         /// <returns></returns>
39         public float Average()
40         {
41             return (this.Chinese + this.Math + this.English) / 3f;
42         }
43 
44         // Properties
45         public string Name
46         {
47             get { return this._name; }
48             set { this._name = value; }
49         }
50 
51         public string Sex
52         {
53             get { return _sex; }
54             set { _sex = value; }
55         }
56 
57         public int Age
58         {
59             get { return _age; }
60             set { _age = value; }
61         }
62 
63         public float Chinese
64         {
65             get { return _chinese; }
66             set { _chinese = value; }
67         }
68 
69         public float Math
70         {
71             get { return _math; }
72             set { _math = value; }
73         }
74 
75         public float English
76         {
77             get { return _english; }
78             set { _english = value; }
79         }
80     }
81 }

四、只读属性和只写属性

  1.只读属性:只有get访问器的属性称为只读属性。该属性使得只能将一项数据从类或类的实例传出,而不能有其它访问。

        public string Name
        {
            get { return this._name; }             
        }

  2.只写属性:只有set访问器的属性称为只写属性。该属性值允许数据从类的外部传入类。

1         public string Name
2         {
3             set { this._name = value; }
4         }

  3.属性必须有至少一个访问器,要么既可读、又可写,要么提供一个。不然编译器会报错。下面是我将Name属性的set访问器和get访问器都去掉之后报的一条错误消息:

五、属性与公共字段的比较

  属性结合了字段和方法的多个方面。对于对象,属性显示为字段,访问属性需要与访问字段相同的语法。对于类的实现者(类本身),属性是一个或两个代码块,表示一个 get 访问器和/或一个 set 访问器。访问器可以对数据的访问提供一些限制和流程控制。

  以下两点表明在实际编码中使用属性更好、更灵活:

    1.字段是数据成员,只有存储能力,没有处理数据能力;而属性是函数成员,可以通过访问器对数据的输入输出进行控制。

    2.程序编译之后,字段和属性的语义不同。

六、自动实现属性

  1.从C#3.0中增加了自动实现属性(automatically implemented property),自动属性就是只声明属性而不声明后备字段,编译器会自动创建隐式的后备字段,并且自动与set和get访问器相关联。当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁。实际开发中,很多都是使用自动属性的。

  2.自动实现属性的特征:

    1).不必声明后备字段,编译器会根据属性的类型自动生成隐式的后备字段来存储数据。

    2).不能提供访问器的方法体,set访问器和get访问器的方法体被一个分号代替。这也是符合逻辑的,因为都不知道后备字段是什么,当然无法对它提供访问控制了,这一切都交给了编译器。

    3).必须同时提供读写属性,缺一不可。

  3.示例

1         // 自动实现属性
2         public string Name
3         {
4             // 只有简单的get;和set;没有提供方法体
5             get;
6             set;
7         }

  4.自动实现属性的内部实现

View Code
 1 // 由编译器生成的set访问器
 2 [CompilerGenerated]
 3 public void set_Name(string value)
 4 {
 5     this.<Name>k__BackingField = value;
 6 }
 7 
 8 // 编译器生成的get访问器
 9 [CompilerGenerated]
10 public string get_Name()
11 {
12     return this.<Name>k__BackingField;
13 }

  通过上面的代码可以看出,编译器为自动属性Name生成了名为“”的后备字段,并且生成了set_Name和get_Name两个访问器。

七、静态属性

  1.静态属性与声明方式

    可以在属性声明的类型前面加上static关键字声明静态属性。静态属性的特征如下:

      1).不能访问类的实例成员。

      2).不管类是否有实例,静态属性都是存在的。

      3).访问方式:从类的内部访问时,与访问其他类成员一样;从类的外部访问时,需要使用“类型名.成员名”的方式。

  2.示例

View Code
 1     class Trivial
 2     {
 3         // 声明一个静态自动实现属性
 4         public static int MyVale { get; set; }
 5 
 6         // 声明一个实例方法
 7         public void PrintValue()
 8         {
 9             // 从类内部的实例方法访问静态属性:与访问其他成员一样。
10             Console.WriteLine("MyValue = {0}", MyVale);
11         }
12     }
13     class Program
14     {
15         static void Main(string[] args)
16         {
17             #region MyRegion
18             TimePeriod t = new TimePeriod();
19 
20             // 为属性赋值,隐式调用set访问器.
21             t.Hours = 24;
22 
23             // 读取属性值,隐式调用get访问器.
24             System.Console.WriteLine("Time in hours: " + t.Hours); 
25             #endregion
26 
27             // 从类的外部访问静态属性:使用"类名.属性名"的访问方式
28             Trivial.MyVale = 10;
29             Console.WriteLine(Trivial.MyVale);
30         }
31     }

 

posted @ 2013-01-01 20:54  YunshiSun  阅读(581)  评论(0编辑  收藏  举报