1封装是什么
封装就是把一个或几个东西装进一个包里,这个包可以是实物,也可以是逻辑上的,比如一个类。目的是不让外面随便碰里面的实现细节。
视频里用一台面积仪代表类 Rectangle:两个读数窗记着长度和宽度,叫成员变量;算面积的齿轮和负责显示的按钮,教程叫成员函数,C# 里一般叫方法。机壳外的控制台代表 Main 方法,它在类外面使用这个类。
2抽象与访问修饰符
抽象是只把使用者需要的东西摆到外面;封装是把其余部分藏起来,让抽象真正做到。藏到什么程度,由访问修饰符决定:它写在成员前面,规定这个成员在哪里能被看到、被使用。
C# 有六种访问修饰符:public、private、protected、internal、protected internal,还有 private protected(学继承时会用到)。
3public:外面都能访问
length 和 width 前面写了 public,Main 可以创建实例(用类造出来的一个具体对象)后直接给它们赋值。
| 27 | Rectangle r = new Rectangle(); | // 用 new 创建 Rectangle 的实例 r |
| 28 | r.length = 4.5; | // length 是 public,类外面能直接赋值 |
| 29 | r.width = 3.5; | // width 也是 public |
| 30 | r.Display(); | // Display 是 public,Main 能通过 r 调用它 |
查看完整代码(34 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | | |
| 3 | namespace RectangleApplication | // 定义命名空间 RectangleApplication |
| 4 | { | // 命名空间开始 |
| 5 | class Rectangle | // 定义 Rectangle 类 |
| 6 | { | // Rectangle 类开始 |
| 7 | //成员变量 | |
| 8 | public double length; | // 公有成员变量:长度 |
| 9 | public double width; | // 公有成员变量:宽度 |
| 10 | | |
| 11 | public double GetArea() | // 公有方法 GetArea:算面积 |
| 12 | { | // GetArea 开始 |
| 13 | return length * width; | // 写在类里面,可以直接用 length 和 width |
| 14 | } | // GetArea 结束 |
| 15 | public void Display() | // 公有方法 Display:显示 |
| 16 | { | // Display 开始 |
| 17 | Console.WriteLine("长度: {0}", length); | // {0} 处填入 length 的值 |
| 18 | Console.WriteLine("宽度: {0}", width); | // 输出宽度 |
| 19 | Console.WriteLine("面积: {0}", GetArea()); | // 调用 GetArea 算出面积再输出 |
| 20 | } | // Display 结束 |
| 21 | }// Rectangle 结束 | // Rectangle 类结束 |
| 22 | | |
| 23 | class ExecuteRectangle | // 定义 ExecuteRectangle 类,放 Main |
| 24 | { | // ExecuteRectangle 类开始 |
| 25 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 26 | { | // Main 开始 |
| 27 | Rectangle r = new Rectangle(); | // 创建实例 r |
| 28 | r.length = 4.5; | // 直接给 length 赋值 4.5 |
| 29 | r.width = 3.5; | // 直接给 width 赋值 3.5 |
| 30 | r.Display(); | // 调用 Display:4.5 × 3.5 = 15.75 |
| 31 | Console.ReadLine(); | // 等待回车(现在的 VS 不写也会停住) |
| 32 | } | // Main 结束 |
| 33 | } | // ExecuteRectangle 类结束 |
| 34 | } | // 命名空间结束 |
长度: 4.5 宽度: 3.5 面积: 15.75
public 很方便,可外面谁都能把长度随手改掉。
4private:只有这个类自己能访问
把 length 改成 private,有了实例 r 也不能用 r. 访问:
| 43 | r.length = 4.5; | // length 是 private,在 Rectangle 外面访问 |
Program.cs(43,15): error CS0122: “Rectangle.length”不可访问,因为它具有一定的保护级别
数据靠这个类自己提供的公有方法进去。教程第二个例子加了公有方法 AcceptDetails,从键盘读入长度和宽度:
| 8 | private double length; | // 私有成员变量:外面够不着 |
| 9 | private double width; | // 私有成员变量:宽度 |
| 10 | | |
| 11 | // 公有方法,用于从用户输入获取矩形的长度和宽度 | |
| 12 | public void AcceptDetails() | // 公有方法,Main 能调用 |
| 13 | { | // AcceptDetails 开始 |
| 14 | Console.WriteLine("请输入长度:"); | // 提示输入 |
| 15 | length = Convert.ToDouble(Console.ReadLine()); | // ReadLine 读入一行文字,Convert.ToDouble 把它转成小数 |
| 16 | Console.WriteLine("请输入宽度:"); | // 提示输入 |
| 17 | width = Convert.ToDouble(Console.ReadLine()); | // 类里面的方法可以给私有变量赋值 |
查看完整代码(51 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | | |
| 3 | namespace RectangleApplication | // 定义命名空间 |
| 4 | { | // 命名空间开始 |
| 5 | class Rectangle | // 定义 Rectangle 类 |
| 6 | { | // Rectangle 类开始 |
| 7 | // 私有成员变量 | |
| 8 | private double length; | // 私有:长度 |
| 9 | private double width; | // 私有:宽度 |
| 10 | | |
| 11 | // 公有方法,用于从用户输入获取矩形的长度和宽度 | |
| 12 | public void AcceptDetails() | // 公有方法 AcceptDetails |
| 13 | { | // AcceptDetails 开始 |
| 14 | Console.WriteLine("请输入长度:"); | // 提示输入长度 |
| 15 | length = Convert.ToDouble(Console.ReadLine()); | // 读入一行文字并转成小数,存进 length |
| 16 | Console.WriteLine("请输入宽度:"); | // 提示输入宽度 |
| 17 | width = Convert.ToDouble(Console.ReadLine()); | // 读入并存进 width |
| 18 | } | // AcceptDetails 结束 |
| 19 | | |
| 20 | // 公有方法,用于计算矩形的面积 | |
| 21 | public double GetArea() | // 公有方法 GetArea |
| 22 | { | // GetArea 开始 |
| 23 | return length * width; | // 返回长 × 宽 |
| 24 | } | // GetArea 结束 |
| 25 | | |
| 26 | // 公有方法,用于显示矩形的属性和面积 | |
| 27 | public void Display() | // 公有方法 Display |
| 28 | { | // Display 开始 |
| 29 | Console.WriteLine("长度: {0}", length); | // 输出长度 |
| 30 | Console.WriteLine("宽度: {0}", width); | // 输出宽度 |
| 31 | Console.WriteLine("面积: {0}", GetArea()); | // 输出面积 |
| 32 | } | // Display 结束 |
| 33 | }//end class Rectangle | // Rectangle 类结束 |
| 34 | | |
| 35 | class ExecuteRectangle | // 定义 ExecuteRectangle 类 |
| 36 | { | // ExecuteRectangle 类开始 |
| 37 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 38 | { | // Main 开始 |
| 39 | // 创建 Rectangle 类的实例 | |
| 40 | Rectangle r = new Rectangle(); | // 创建实例 r |
| 41 | | |
| 42 | // 通过公有方法 AcceptDetails() 从用户输入获取矩形的长度和宽度 | |
| 43 | r.AcceptDetails(); | // 只调用公有方法,碰不到私有变量 |
| 44 | | |
| 45 | // 通过公有方法 Display() 显示矩形的属性和面积 | |
| 46 | r.Display(); | // 显示结果 |
| 47 | | |
| 48 | Console.ReadLine(); | // 等待回车(现在的 VS 不写也会停住) |
| 49 | } | // Main 结束 |
| 50 | } | // ExecuteRectangle 类结束 |
| 51 | } | // 命名空间结束 |
输入 5 和 3(下面的 5 和 3 是键盘输入的):
请输入长度: 5 请输入宽度: 3 长度: 5 宽度: 3 面积: 15
这就是封装:数据藏在里面,只通过公有方法来操作。
private 按整个类划分:写在 Rectangle 里的方法,能直接读另一个 Rectangle 对象的私有成员:
| 16 | public bool SameSize(Rectangle other) | // 参数 other 是另一个 Rectangle 对象 |
| 17 | { | // SameSize 开始 |
| 18 | return length == other.length && width == other.width; | // 直接读 other 的私有成员,&& 表示两个都成立 |
| 19 | } | // SameSize 结束 |
| 26 | Rectangle a = new Rectangle(4.5, 3.5); | // 括号里的值交给构造函数(后面会讲):长 4.5、宽 3.5 |
| 27 | Rectangle b = new Rectangle(4.5, 3.5); | // b:长宽一样 |
| 28 | Console.WriteLine(a.SameSize(b)); | // 长宽都相等,输出 True |
True
5protected:自己和子类能访问
受保护的成员,这个类自己和它的子类能访问。子类是从别的类派生出来的,比如正方形是一种矩形:
| 7 | protected double length = 4.5; | // 受保护的成员变量,初值 4.5 |
| 8 | } | // Rectangle 类结束 |
| 9 | | |
| 10 | class Square : Rectangle | // Square 是 Rectangle 的子类(冒号后面写父类) |
| 11 | { | // Square 类开始 |
| 12 | public void Show() | // Square 的方法 Show |
| 13 | { | // Show 开始 |
| 14 | Console.WriteLine("长度: {0}", length); | // 子类里能直接读到 length |
| 15 | } | // Show 结束 |
长度: 4.5
Main 不在子类里,直接访问 r.,照样报同一个错:
| 25 | r.length = 3.5; | // Main 不在 Rectangle 或它的子类里 |
Program.cs(25,15): error CS0122: “Rectangle.length”不可访问,因为它具有一定的保护级别
protected 主要用在继承上,到继承那一章再细讲。
6internal:同一个程序集能访问
程序集就是一个项目编译出来的 EXE 或 DLL 文件。internal 成员在同一个程序集里都能访问;一个应用程序可以由好几个程序集组成。
| 8 | internal double length; | // internal:同一个程序集里都能访问 |
| 9 | internal double width; | // 宽度也是 internal |
| 10 | | |
| 11 | double GetArea() | // 前面没写修饰符,默认是 private |
| 12 | { | // GetArea 开始 |
| 13 | return length * width; | // 返回长 × 宽 |
| 14 | } | // GetArea 结束 |
| 27 | r.length = 4.5; | // Main 和 Rectangle 在同一个项目里,能直接赋值 |
| 28 | r.width = 3.5; | // width 也是 internal,同样能赋值 |
| 29 | r.Display(); | // 输出长度 4.5、宽度 3.5、面积 15.75,和 public 那次一样 |
GetArea 不写修饰符,默认是 private,所以 Display 能调用它,Main 一调用就报同一个错:
| 30 | Console.WriteLine(r.GetArea()); | // 在 Main 里调用默认 private 的 GetArea |
Program.cs(30,33): error CS0122: “Rectangle.GetArea()”不可访问,因为它具有一定的保护级别
7protected internal
两种范围合在一起:同一个程序集里能访问;别的程序集里,只要是这个类的子类,也能访问。
8小结
- public 哪里都能用,private 只有这个类自己能用。
- protected 加上子类,internal 限在同一个程序集,protected internal 是这两种合起来。
- 常见做法:数据设成 private,再用 public 方法把该给的功能露出来。
下一节:一个方法怎么定义、怎么调用,又怎么把结果交回来?
