C#面向对象之封装

一、封装

  下面我设计了一个空调类,对于要使用这个空调类的其他类我可以称它们为用户。用户需要了解空调类的使用方法,才能更好的使用空调。空调类如下:

 1 public class Air
 2 {
 3     /// <summary>
 4     /// 空调温度
 5     /// </summary>
 6     public int temperature;
 7 
 8     /// <summary>
 9     /// 空调上下方向(用int量化方向,0代表下,1代表中下,2代表中,3代表中上,4代表上)
10     /// </summary>
11     public int verticalDirection;
12 
13     /// <summary>
14     /// 空调开关(false代表关,true代表开)
15     /// </summary>
16     public bool switch_C;
17 
18     public Air()
19     {
20         temperature = 28;
21         verticalDirection = 3;
22         switch_C = false;
23     }
24 }

用户使用我设计的空调类,如果他对使用方法不太熟,它可能给空调上下方向设置成5或者其它数字;或者今天天气特别热,它把空调温度设置成0度甚至更低,或者失误设置成100度。

 1 public class User
 2 {
 3     public void UseAir()
 4     {
 5         Air air = new Air();
 6         air.switch_C = true;
 7         air.verticalDirection = 5;  //空调上下方向没有5这个设置
 8         air.temperature = 0;        //空调温度设置成0度
 9         air.temperature = 100;      //如果我的空调类什么温度都能达到,那可能要用户老命!!!
10     }
11 }

可以看出我的空调类设计得有问题,我应该限制空调温度和空调上下方向的设置范围,那么首先用户不能直接设置它们的值,所以我要把空调温度和空调上下方向定义成私有的,然后设计公共方法来限制它们的设置范围,同时还要设计公共方法为用户提供查询操作。空调开关也可以通过这种方式简化用户操作。第二版空调类如下:

 1 public class Air
 2 {
 3     /// <summary>
 4     /// 空调温度
 5     /// </summary>
 6     private int temperature;
 7 
 8     /// <summary>
 9     /// 空调上下方向(用int量化方向,0代表下,1代表中下,2代表中,3代表中上,4代表上)
10     /// </summary>
11     private int verticalDirection;
12 
13     /// <summary>
14     /// 空调开关(false代表关,true代表开)
15     /// </summary>
16     private bool switch_C;
17 
18 
19     /// <summary>
20     /// 获取空调温度
21     /// </summary>
22     public int GetTemperature()
23     {
24         return temperature;
25     }
26 
27     /// <summary>
28     /// 设置空调温度
29     /// </summary>
30     /// <param name="degree"></param>
31     public void SetTemperature(int degree)
32     {
33         if (degree > 16 && degree < 30)
34         {
35             temperature = degree;
36         }
37         else
38         {
39             //提示设置错误
40         }
41     }
42 
43     /// <summary>
44     /// 获取空调上下方向
45     /// </summary>
46     /// <returns></returns>
47     public int GetVerticalDirection()
48     {
49         return verticalDirection;
50     }
51 
52     /// <summary>
53     /// 设置空调上下方向
54     /// </summary>
55     /// <param name="direction"></param>
56     public void SetVerticalDirection(int direction)
57     {
58         if (direction >= 0 && direction < 5)
59         {
60             verticalDirection = direction;
61         }
62         else
63         {
64             //提示设置错误
65         }
66     }
67 
68     public bool GetSwitch()
69     {
70         return switch_C;
71     }
72 
73     public void SetSwitch()
74     {
75         switch_C = switch_C ? false : true;
76     }
77 
78     //---设置有参构造函数时,调用相应set方法做对应限制---
79     public Air(int num, int upOrdown, bool openOrClose)
80     {
81         SetTemperature(num);
82         SetVerticalDirection(upOrdown);
83         switch_C = openOrClose;
84     }
85 }

上面我设计的第二版空调类所做的事就是封装,通过相应的Get和Set方法对外公开数据的操作,又能做到相应的限制,即保护字段数据不出现意外的值,又能对外隐藏操作数据方式的细节(无论SetTemperature方法的实现多么复杂,用户只需要知道如何使用就行了,好比你使用空调时只需要知道如何使用空调的功能,而不需要知道空调是如何完成这些功能的)。

二、属性

  属性是对Get方法和Set方法的简化形式,也是C#提供的强制封装字段的方式。除此之外,它的最大优势是可以像使用字段一样使用属性。只读属性和只写属性即只有Get方法的属性和只有Set方法的属性。属性版空调类如下:

 1 public class Air
 2 {
 3     private int temperature;
 4     private int verticalDirection;
 5     private bool switch_C;
 6 
 7     /// <summary>
 8     /// 空调温度的属性(属性的返回值类型同对应的字段类型,属性的名字一般首字母大写)
 9     /// </summary>
10     public int Temperature
11     {
12         //get作用域等同GetTemperature()方法
13         get { return temperature; }
14         //set作用域等同SetTemperature(int degree)方法,value等于参数degree
15         set
16         {
17             if (value > 16 && value < 30)
18             {
19                 temperature = value;
20             }
21             else
22             {
23                 //提示设置错误
24             }
25         }
26     }
27 
28     /// <summary>
29     /// 空调上下方向的属性
30     /// </summary>
31     public int VerticalDirection
32     {
33         get { return verticalDirection; }
34         set
35         {
36             if (value >= 0 && value < 5)
37             {
38                 verticalDirection = value;
39             }
40             else
41             {
42                 //提示设置错误
43             }
44         }
45     }
46 
47     /// <summary>
48     /// 空调开关的属性
49     /// </summary>
50     public bool Switch_C
51     {
52         //只读属性
53         get { return switch_C; }
54     }
55 
56     //SetSwitch方法没有bool参数,不能简化成属性,它是一个普通方法
57     public void SetSwitch()
58     {
59         switch_C = switch_C ? false : true;
60     }
61 
62     #region 构造函数直接调用属性,可以做到各司其职,构造函数只做初始化,检查字段有效范围则交给属性
63     public Air() : this(28, 3, false) { }
64 
65     public Air(int num, int upOrdown, bool openOrClose)
66     {
67         Temperature = num;
68         VerticalDirection = upOrdown;
69         //只读属性只有Get方法没有Set方法,所以无法通过构造函数赋值,只能通过为其私有字段赋值,间接为属性赋值
70         switch_C = openOrClose;
71     }
72     #endregion
73 }
74 public class User
75 {
76     public void UseAir()
77     {
78         Air air = new Air();
79         //开启空调
80         air.Switch_C();
81         //像字段一样使用属性
82         air.Temperature += 1;
83         air.VerticalDirection = 0;
84     }
85 }

   当你获取属性的值时执行属性的get作用域,背后其实调用的是一个无参数且返回值类型同其封装的字段类型的方法;当你设置属性的值时执行属性的set作用域,背后其实调用的是一个有且仅有一个类型同其封装的字段类型的参数且返回值为void的方法。

三、自动属性

  当属性只对封装字段作简单的包装时,可以使用自动属性简化封装字段数据的过程。结构如下:

1 //private int duration;
2 public int Duration
3 {
4     get; //{ return duration; }
5     set; //{ duration = value; }
6 }

自动属性省略了定义私有字段和get/set实现逻辑,这些省略的部分会由编译器在编译代码是为其自动生成。

注:在C#6.0语法以前自动属性不能设计成只读或只写自动属性,C#6.0语法开始允许设计只读自动属性。代码如下:

 1 /// <summary>
 2 /// C#6.0以前
 3 /// </summary>
 4 public class Air
 5 {
 6     //不能设计只读或只写自动属性
 7     //public int Temperature { get; }
 8     //public int VerticalDirection { set; }
 9     //只能把相应的部分设计成private的
10     public int Temperature { get; private set; }
11     public int VerticalDirection { private get; set; }
12 
13     public Air(int num, int upOrdown)
14     {
15         //不能设计成只读自动属性的原因:只有Get方法没有Set方法无法赋值,所以永远不会有值。
16         //Temperature = num;
17         //不能设计成只写自动属性的原因:只有Set方法没有Get方法可以赋值,但永远无法被使用。
18         VerticalDirection = upOrdown;
19     }
20 }
21 /// <summary>
22 /// C#6.0开始
23 /// </summary>
24 public class Air
25 {
26     //可以设计只读自动属性,但依然不能设计只写自动属性
27     public int Temperature { get; }
28     public int VerticalDirection { private get; set; }
29 }

疑问解答:自动属性存在的意义?控制可变的风险。自动属性实现封装服务的第二种作用,对外隐藏操作数据方式的细节。如果我直接使用字段定义数据,后期该字段业务需求发生改变需要做一定的限制,则需要添加方法来作限制,而此时使用这个字段的用户成千上万,全部从使用字段修改到使用方法代价极大;而使用自动属性则只需要在属性内添加限制即可,因为自动属性不写业务或写业务对用户使用起来没有区别。

原文地址:https://www.cnblogs.com/yaojieyuan/p/11469603.html