C# 8.0本质论
上QQ阅读APP看书,第一时间看更新

6.1 类的声明和实例化

定义类首先指定关键字class,后跟一个标识符,如代码清单6.1所示。

代码清单6.1 定义类

该类的所有代码放到类声明之后的大括号中。虽然并非必须,但一般应该将每个类都放到它自己的文件中,用类名对文件进行命名。这样可以更容易地寻找定义了一个特定类的代码。

设计规范

·不要在一个源代码文件中放多个类。

·要用所含公共类型的名称命名源代码文件。

定义好新类后,就可以像使用.NET Framework内置的类那样使用它了。换言之,可声明该类型的变量,或定义方法来接收该类型的参数。代码清单6.2对此进行了演示。

代码清单6.2 声明类类型的变量

初学者主题:对象和类

在非正式场合,类和对象这两个词经常互换着使用。但它们具有截然不同的含义。是模板,定义了对象在实例化时看起来像什么样子。所以,对象是类的实例。类就像模具,定义了零件的样子。对象就是用这个模具创建的零件。从类创建对象的过程称为实例化,因为对象是类的实例。

现已定义了一个新的类类型,接着可实例化该类型的对象。效仿它的前任语言,C#使用new关键字实例化对象(参见代码清单6.3)。

代码清单6.3 实例化一个类

毫不奇怪,声明和赋值既能在同一行上完成,也能分行完成。

和以前使用的基元数据类型(如int)不同,不能用字面值指定一个Employee。相反,要用new操作符指示“运行时”为Employee对象分配内存、初始化对象,并返回对实例的引用。

虽然有专门的new操作符分配内存,但没有对应的操作符回收内存。相反,“运行时”会在对象变得不可访问之后的某个时间自动回收内存。具体是由垃圾回收器回收。它判断哪些对象不再由其他活动对象引用,然后安排一个时间回收对象占用的内存。这样就不能在编译时判断在程序的什么位置回收并归还内存。

这个简单的例子中没有数据或方法与Employee关联,这样的对象完全没用。下一节重点讲述如何为对象添加数据。

初学者主题:封装(第一部分)——对象将数据和方法组合到一起

假定接收到一叠写有员工名字的索引卡、一叠写有员工姓氏的索引卡以及一叠写有他们工资的索引卡,那么除非知道每一叠卡片都按相同顺序排列,否则这些索引卡没有什么作用。即使符合这个条件,也很难使用上面的数据,因为要判断一个人的全名需要搜索两叠卡片。更糟的是,如丢掉其中的一叠卡片,就没有办法再将名字与姓氏和工资关联起来。这时需要的是一叠员工卡片,每个员工的数据都组合到一张卡片中。换言之,要将名字、姓氏和工资封装到一起。

日常生活中的封装是将一系列物品装入封套。类似地,面向对象编程将方法和数据装入对象。这提供了所有类成员(类的数据和方法)的一个分组,使它们不再需要单独处理。不需要将名字、姓氏和工资作为三个单独的参数传给方法。相反,可在调用时传递对一个员工对象的引用。一旦被调用的方法接收到对象引用,就可以向对象发送消息(例如调用像AdjustSalary()这样的方法)来执行特定的操作。

语言对比:C++——delete操作符

程序员应将new的作用理解成实例化对象而不是分配内存。在堆和栈上分配对象都支持new操作符,这进一步强调了new不是关于内存分配的,也不是关于是否有必要进行回收的。

所以,C#不需要C++中的delete操作符。内存分配和回收是“运行时”的细节。这使开发者可以将注意力更多地放在业务逻辑上。然而,虽然“运行时”会管理内存,但它不会管理其他资源,比如数据库连接、网络端口等。和C++不同,C#不支持隐式确定性资源清理(在编译时确定的位置进行隐式对象析构)。幸好,C#通过using语句支持显式确定性资源清理,通过终结器支持隐式非确定性资源清理。