C#中的异常处理二

Author:Jon Jagger  From:Internet

三、finally?
    解决释放问题的方法依靠你现在使用的语言。在C  中,你可以使用构建于堆栈上的析构函数。Java中,你能构使用finally程序块。C#允许你创造自定义的结构类型但是不允许结构中的析构函数(只是因为一个C#析构函数其实是一个Finally方法,Finally被垃圾回收器调用。结构类,是一种值类型,并不归属于垃圾回收器回收的范围)。因而,只是在开始,C#必须追循Java的道路,使用finally程序块。首先,我们的finally程序块开起来如下:
private static char[] ReadSource(string filename)
{
  try
  {
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  char[] source = new char[length];
  TextReader reader = file.OpenText();
  reader.Read(source, 0, length);
  }
  finally
  {
  reader.Close();
  }
  return source;
}
  这个版本不得不引入一个try程序块(既然一个finally程序快必须跟随在一个try程序块后),这将是一个合理的解决方案,如果它奏效的话。但是,它没有做到。问题是try程序块构建成一个范围,所以在finally程序块中的reader并不在这个范围内并且返回语句中的source也不在这个范围。
 
  finally?
  为了解决这个问题,你不得不将reader和source的声明移到try程序块的外面,第二次尝试如下:
private static char[] ReadSource(string filename)
{
  TextReader reader;
  char[] source;
  try
  {
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  source = new char[length];
  reader = file.OpenText();
  reader.Read(source, 0, length);
  }
  finally
  {
  reader.Close();
  }
  return source;
}
  这个版本将reader和source的声明移到了try程序块的外面,接着指派给reader和source但没有初始化它们。这是和开始的“理想”版本不同的另外一个地方(出现两个多余行)。然而,你可能认为如果它工作,那将是一个合理的解决方案。但是它没有。问题是委派并不等同于初始化及让编译器知道它。如果在reader被分配之前出现一个异常,这是在finally程序块中对reader.close()的调用
将根据没有被分配的reader,C#,像Java一样,不允许那样。 

    finally?
    很明显,你必须要初始化reader,第三次尝试如下:
private static char[] ReadSource(string filename)
{
  TextReader reader = null;
  char[] source;
  try
  {
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  source = new char[length];
  reader = file.OpenText();
  reader.Read(source, 0, length);
  }
  finally
  {
  reader.Close();
  }
  return source;
}
  这个版本引入了空值,这没有出现在最初的“理想版本”中。不过,如果你仍然认为如果它起作用这将是一个合理的解决方式,然而它不是(虽然它能通过编译)。问题是你在调用reader.close()的时候很容易抛出NullReferenceException异常。 

  finally?
  一种解决方法是对eader.close()方法进行保护,下面做第四次尝试:
private static char[] ReadSource(string filename)
{
  TextReader reader = null;
  char[] source;
  try
  {
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  source = new char[length];
  reader = file.OpenText();
  reader.Read(source, 0, length);
  }
  finally
  {
  if (reader != null)
  {
    reader.Close();
  }
  }
  return source;
}
  当然,对reader.close()的保护并不是ReadSource的理想版本。但是,如果仅从它的效果上看,这将是一个合理的版本。最终,工作。它和最初的版本已经大不相同。稍微努力,你能复用下面这段代码:
private static char[] ReadSource(string filename)
{
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  char[] source = new char[length];
  TextReader reader = file.OpenText();
  try
  {
  reader.Read(source, 0, length);
  }
  finally
  {
  if (reader != null)
  {
    reader.Close();
  }
  }
  return source;
}
  在某些情况下,你可以在finally程序块中对可能出现空值进行判断(上面就是一个这样的例子),但是一般说来,你最好还是在finally程序块中判断一下(考虑到如果file.OpenText返回空值,或者如果reader被放进了try程序块中,或者reader作为ref/out参数被传递到try程序块中)。你不得不增加一个try程序块,一个finally程序块,和一个if防护。如果你正在使用Java,你不得不每一次都去做这些事情。在那里是个最大的问题。如果这个解决方案非常令人厌烦且完全偏离于最初的“完美”方案,这没有关系,你能够将这些异常提取到一块。在Java中,你不能这么做。遇到异常,Java将停止运行,而C#则将继续。 

四、using语句 

    在C#中,最接近于“理想”版本的是使用using语句:
private static char[] ReadSource(string filename)
{
  FileInfo file = new FileInfo(filename);
  int length = (int)file.Length;
  char[] source = new char[length];
  using (TextReader reader = file.OpenText())
  {
  reader.Read(source, 0, length);
  }
  return source;
}
  Reader将会被恰当的关闭。简单说来,using语句有大量的特征能够改善开始的“理想”版本。首先,我们看一下它内在的运行机制到底是怎样的。
using语句转换
    C#ECMA标准描述using声明:
using (type variable = initialization)
    embeddedStatement
它等同于
{
  type variable = initialization;
  try
  {
  embeddedStatement
  }
  finally
  {
  if (variable != null)
  {
    ((IDisposable)variable).Dispose();
  }
  }
}
它依赖于System命名空间中的IDisposable接口:
namespace System
{
  public interface IDisposable
  {
  void Dispose();
  }
}
    注意:finally程序块中的牵制转换意味着,这个变量必须是一个支持IDisposable接口的类(通过继承或转换操作)。如果它不是,你就会得到一个编译时错误。
using TextReader 转换
  不出乎意料,TextReader支持IDisposable接口,并且实现了Dispose来调用关闭。这意味着:
using (TextReader reader = file.OpenText())
{
  reader.Read(source, 0, length);
}
相当于下面:
{
  TextReader reader = file.OpenText();
  try
  {
  reader.Read(source, 0, length);
  }
  finally
  {
  if (reader != null)
  {
    ((IDisposable)reader).Dispose();
  }
  }
}
  除了对IDisposable的强制转换外,这和最通用的Java解决方式是相同的。这个强制转换是必须的因为这是一个通用解决方式。

 感谢原创者的辛勤劳动,希望对您有所帮助,转载请注明原出处。
 您可能对 [C#] 的这些文章也感兴趣:

C#锐利体验(3.1)
透过IL看C# (2)——switch语句(下)
论C#变得越来越臃肿是不可避免的
C# 4.0新特性(中英文对照白皮书)
C# 实现Socket5代理协议通讯
C#进阶(九)
.NETDelegates:AC#BedtimeStory中文版(下篇)
自定义C#加密方法
VisualStudio.Net内幕(1)
实例介绍C#GUI开发(2)