首页 > 代码库 > 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 接口清理资源