首页 > 代码库 > 9.3.2 用 IDisposable 接口清理资源
9.3.2 用 IDisposable 接口清理资源
9.3.2 用 IDisposable 接口清理资源
我们已经使用过几种实现了 IDisposable 接口的类型,比如,Graphics 和 SolidBrush。我们想使代码尽可能易于理解,因此,当使用完对象时,我们就显式调用 Dispose 方法。
C# 中包含了语法糖,以 using 语句的形式,保证了即使语句体内抛出异常,也能调用 Dispose;F# 有相似的结构,用 use 关键字。清单 9.11 是一个处理文件的简单示例。
Listing 9.11 使用 use 关键字处理文件 (F#Interactive)
> open System.IO;;
> let readFile() =
use reader = newStreamReader("C:\\test.txt") [1]
let text =reader.ReadToEnd()
Console.Write(text)
;; [2]
val readFile : unit –> unit
> readFile();; | 输出样本文件的内容
Hello world! |
Ahoj svete! |
创建 StreamReader(实现了 IDisposable 接口),我们使用 use 关键字声明[1]。注意,这个语法类似于通常 let 中使用的 let 关键字;所不同的是,F# 编译器会在函数的结尾[2],自动添加对 Dispose 方法的调用,因此,在我们处理完 StreamReader 之后,它会自动释放。编译器还插入一个 try - finally 块,以确保即使在发生异常时,也能进行清理。
C# 中的 using 结构和 F# 中的 use 关键字之间的重要区别,是在C# 中,必须明确地使用大括号指定 using 的作用域;而在 F# 中,Dispose 方法是在值可见的作用域结束时,被调用,通常这就是我们所需要的,因此,写许多代码就变得很容易了。清单 9.12 展示了 C# 和 F# 的版本。
清单 9.12 在 F# 和 C# 中清理资源
// F# version
let test() =
use reader = newStreamReader("C:\\test.txt")
let text = reader.ReadToEnd()
Console.Write(text) [1]
// C# version
void Test() {
using(var reader = newStreamReader("C:\\test.txt")) {
var text =reader.ReadToEnd();
Console.Write(text);
} [2]
}
在两种语言中,对象都是在运行到离开 reader 值可以访问的作用域时[1][2],被释放的。在 F# 中,默认是在函数结束时发生,这通常就是我们所需要的。当函数的代码需要连续运行很长时间时,它更好地保证了资源尽早释放。让我来说,是想关闭该文件,在关闭后,再输出内容到控制台。在 C# 中,我们必须在函数内部创建一个局部变量,并在 using 块内给它赋值;在 F# 中,可以更容易做到,因为,我们可以使用缩进指定作用域:
let test() =
let text =
use reader = newStreamReader("C:\\test.txt")
reader.ReadToEnd()
Console.Write(text)
这个语法可能有些出人意料,但是,一旦我们理解了,在 F# 中,每一个代码块就是一个表达式,它就变得清晰了。在前面的代码中,我们指定构造这个表达式的方式,与我们写 (1+2)*3,而不是默认的1+(2*3) 的方式,是相同的。这样,我们可以限制值 reader 的作用域到初始化值 text 的表达式。
虽然 use 关键字主要用于处理持有一些资源 .NET 对象,但是,它使用范围很广泛。让我们来看一个例子。
9.3.2 用 IDisposable 接口清理资源