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实现逻辑,这些省略的部分会由编译器在编译代码是为其自动生成。
注:自动属性不应该设计成只读或只写自动属性,因为自动属性的私有字段只有在编译时才生成,写代码时没有私有字段。代码如下:
1 public class air 2 { 3 public int temperature{ get; } 4 public int verticaldirection { set; } 5 6 public air(int num, int upordown) 7 { 8 //只读自动属性无法赋值,所以永远不会有值。 9 //temperature = num; 10 //只写自动属性可以赋值,但永远无法被使用。 11 verticaldirection = upordown; 12 } 13 }
上一篇: 为什么使用消息队列?消息对列有什么好处?