科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件C#中的异常处理(二)

C#中的异常处理(二)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

解决释放问题的方法依靠你现在使用的语言。在C++中,你可以使用构建于堆栈上的析构函数。

作者:ivsee 来源:soft6 2008年5月16日

关键字: 处理 异常 C# Windows

  • 评论
  • 分享微博
  • 分享邮件
 三、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解决方式是相同的。这个强制转换是必须的因为这是一个通用解决方式。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章