
6.4 使用this关键字
可在类的实例成员内部获取对该类的引用。C#允许用关键字this显式指出当前访问的字段或方法是包容类的实例成员。调用任何实例成员时this都是隐含的,它返回对象本身的实例。来看看代码清单6.9中的SetName()方法。
代码清单6.9 使用this显式标识字段的所有者

本例使用关键字this指出字段FirstName和LastName是类的实例成员。
虽然可为所有本地类成员引用添加this前缀,但设计规范是若非必要就不要在代码中“添乱”。所以,this关键字只在必要时才应使用。本章后面的代码清单6.12是必须使用this的例子。代码清单6.9和6.10则不是。在代码清单6.9中,舍弃this不会改变代码含义。而代码清单6.10可修改字段的命名规范并遵循参数的命名规范来避免局部变量和字段之间的歧义。
初学者主题:依靠编码样式避免歧义
在SetName()方法中没必要使用this关键字,因为FirstName显然有别于newFirstName。但现在假定参数不叫做newFirstName,而叫做FirstName(使用PascalCase风格的大小写规范),如代码清单6.10所示。
代码清单6.10 使用this避免歧义

本例要引用FirstName字段就必须显式指明它所在的Employee对象。this就像在Program.Main()方法中使用的employee1变量前缀(参见代码清单6.8),它引用要在其上调用SetName()方法的对象。
代码清单6.10不符合C#命名规范,即参数要像局部变量那样使用camelCase大小写风格来命名(除第一个单词,其他每个单词首字母大写)。这可能造成难以发现的bug,因为将FirstName(本来想引用字段)赋值给FirstName(参数),代码仍能编译并运行。为避免该问题,最好是为参数和局部变量采用与字段和属性不同的命名规范。本章稍后会演示该规范的实际应用。
语言对比:Visual Basic——使用Me访问类的实例
C#关键字this完全等价于Visual Basic关键字Me。
代码清单6.9和代码清单6.10中的GetName()方法没有使用this关键字,它确实可有可无。但假如存在与字段同名的局部变量或参数(参见代码清单6.10的SetName()方法),省略this将访问局部变量或参数而非字段。这时this就是必需的。
还可使用this关键字显式访问类的方法。例如,可在SetName()方法内使用this.GetName()输出新赋值的姓名(参见代码清单6.11和输出6.3)。
代码清单6.11 this作为方法名前缀

输出6.3

有时需要使用this传递对当前对象的引用。如代码清单6.12中的Save()方法所示。
代码清单6.12 在方法调用中传递this


Save()方法调用DataStorage类的Store()方法。但需要向Store()方法传递准备进行持久化存储的Employee对象。这是使用关键字this来完成的,它传递了正在其上调用Save()方法的Employee对象的实例。
存储和载入文件
在DataStorage内部,Store()方法的实现要用到System.IO命名空间中的类,如代码清单6.13所示。Store()内部首先要实例化一个FileStream对象,将它与一个对应员工全名的文件关联。FileMode.Create参数指明如<firstname><lastname>.dat文件不存在,就新建一个;如文件存在,就覆盖它。接着创建一个StreamWriter对象将文本写入FileStream。数据用WriteLine()方法写入,就像向控制台写入一样。
代码清单6.13 将数据持久化存储到文件


注①:该代码可用using语句改进,但因为我们还没有讲到,所以暂时不使用。
写入完成后应关闭FileStream和StreamWriter,避免它们在等待垃圾回收期间处于“不确定性打开”状态。上述代码不含任何错误处理机制,所以如抛出异常,两个Close()方法都得不到调用。
文件载入过程与存储过程相似,如代码清单6.14和输出6.4所示。
代码清单6.14 从文件获取数据


注①:该代码可用using语句改进,但因为我们还没有讲到,所以暂时不使用。
输出6.4

代码清单6.14使用StreamReader而非StreamWriter展示和存储相反的过程。同样地,一旦数据读取完毕,就要在FileStream和StreamReader上调用Close()方法。
输出6.4没有在Inigo Montoya:之后显示任何工资信息,因为只有在调用Save()之后,才会通过调用IncreaseSalary()将Salary设为Enough to survive on。
注意Main()可在员工实例上调用Save(),但载入新员工调用的是DataStorage.Load()。需要载入员工时,一般还没有可在其中载入(数据)的员工实例,所以Employee类的实例方法不可调用。除了在DataStorage类本身上调用Load,另一个办法是为Employee类添加静态Load()方法(详情参见6.8节),这样就可以调用Employee.Load()——注意是直接在Employee类上调用,而不是在它的实例上调用。
注意代码清单顶部包含using System.IO指令,这样可直接访问每个IO类,无须为其附加完整的命名空间前缀。