C#中的异常处理(二 )

    技术2022-05-11  74

    四、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解决方式是相同的。这个强制转换是必须的因为这是一个通用解决方式。

    五、以自定义方式处理    这是有益的当你去考虑如果TextReader没有实现IDisposable接口将会出现什么情况。这篇教程从此处开始将知道我们如何在我们自己的类里实现Dispose的处理。一种方式是使用对象适配器(Object Adapter)模式。例如: public sealed class AutoTextReader : IDisposable {  public AutoTextReader(TextReader target)  {   // PreCondition(target != null);   adaptee = target;  }    public TextReader TextReader  {   get { return adaptee; }  }    public void Dispose()  {    adaptee.Close();  }    private readonly TextReader adaptee;  } 你可以这样使用你自己的类: using (AutoTextReader scoped = new AutoTextReader(file.OpenText())) {  scoped.TextReader.Read(source, 0, length); } 你能够使用隐式转换操作符使问题变得更简单一些:  public sealed class AutoTextReader : IDisposable {  ...  public static implicit operator AutoTextReader(TextReader target)  {   return new AutoTextReader(target);  }  ... } 这将允许你这样使用:  using (AutoTextReader scoped = file.OpenText()) {  scoped.TextReader.Read(source, 0, length); }   struct :另一种选择 AutoTextReader有意使用为密封类,就想它的名字建议的,以用作本地变量。使用一个结构来代替类更加有意义: public struct AutoTextReader : IDisposable {  // exactly as before } 使用一个结构代替类也提供给你几种自由优化。既然一个结构是一个值类型,它能构一直都不是空值。这意味着编译器必须对生成的finally程序块做空值的检测。并且,既然你不能继承于一个结构,它的运行时将和编译时的类型一致。这意味着编译器一般在生成的finally程序块中做强制转换并因而避免了一次装箱操作(特别的,如果Dispose是一个公开的隐式接口实现而不是一个不公开的显示接口实现,这将避免强制转换)。 换句话,就是这样: using (AutoTextReader scoped = file.OpenText()) {  scoped.TextReader.Read(source, 0, length); } 被转换为: {  AutoTextReader scoped = new file.OpenText();  try  {   scoped.TextReader.Read(source, 0, length);  }  finally  {   scoped.Dispose();  } }    由此,我更喜欢使用using语句代替finally程序来处理。事实上,using语句解决方式相较于开始的“理想“版本还有如下额外的几个优点,一个using语句: ·运行中,它能够一直释放资源 ·是一个扩展机制。它允许你创建一个资源释放的集合。创建你自己的资源释放类例如AutoTextReader是容易的。 ·允许你将资源获取和资源释放配对使用。释放资源最好的时刻就是你获得资源的一刻。就像如果你从图书馆借了一本书,你能在你借的时候被告知什么时候归还。】 ·根据句法构造,能够清楚的告诉你你正在使用一个资源。 ·为拥有资源的变量创造一个范围。仔细观察对using语句的编译器转换,你将发现它聪明的使用了一对外部括号。       using (AutoTextReader scoped = file.OpenText())     {          scoped.TextReader.Read(source, 0, length);     }     scoped.TextReader.Close(); // scoped is not in scope here     这是对C++中依据条件声明的追溯。允许你限制变量使用的范围,仅仅在这个范围内,变量是有用的,当变量能够使用,它只能存在于这个范围内。


    最新回复(0)