C#中的异常处理(一) (转)

转之:中国BS网,原文:http://www.chinabs.net/webcsharp/default.asp?infoid=427

一 、令人痛苦的程式化错误处理
    异常还没出现前,处理错误最经典的方式就是使用错误代码检查语句了。

 

None.gif 例如
None.gif
public   sealed   class  Painful
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {  
InBlock.gif    dot.gif  
InBlock.gif    
private static char[] ReadSource(string filename)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        FileInfo file 
= new FileInfo(filename);
InBlock.gif        
if (errorCode == 2342goto handler;
InBlock.gif        
int length = (int)file.Length;  
InBlock.gif        
char[] source = new char[length];
InBlock.gif        
if (errorCode == -734goto handler;
InBlock.gif        TextReader reader 
= file.OpenText();
InBlock.gif        
if (errorCode == 2664goto handler;
InBlock.gif        reader.Read(source, 
0, length);
InBlock.gif        
if (errorCode == -5227goto handler;
InBlock.gif        reader.Close();
InBlock.gif        Process(filename, source); 
InBlock.gif        
return source;
InBlock.gif        handler:
InBlock.gif        dot.gif
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

这种编码方式单调乏味,翻来复去,难以使用,看起来非常的复杂,而且还使得基本功能很不清晰。并且还很容易忽略错误(故意或者偶尔的遗忘)。现在好了,有很多来处理这种情况,但是其中必有一些处理方式要好过其他的。
 
二、关系分离
    异常所能做到的最基本的事情就是允许你将错误和基本功能分离开来。换句话,我们能将上面的改写如下:
None.gif public   sealed   class  PainLess
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {    
InBlock.gif    
public static int Main(string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif           
string filename = args[0];
InBlock.gif           
char[] source = ReadSource(filename);
InBlock.gif           Process(filename, source);
InBlock.gif           
return 0;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
catch (SecurityException    caught) dot.gifdot.gif }
ExpandedSubBlockStart.gifContractedSubBlock.gif        
catch (IOException          caught) dot.gifdot.gif }
ExpandedSubBlockStart.gifContractedSubBlock.gif        
catch (OutOfMemoryException caught) dot.gifdot.gif }
InBlock.gif        dot.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif 
InBlock.gif    
private static char[] ReadSource(string filename)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        FileInfo file 
= new FileInfo(filename);
InBlock.gif        
int length = (int)file.Length;
InBlock.gif        
char[] source = new char[length];
InBlock.gif        TextReader reader 
= file.OpenText();
InBlock.gif        reader.Read(source, 
0, length);
InBlock.gif        reader.Close();
InBlock.gif        
return source;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif


在转化过程中,需要注意以下几点:
1.以前使用数字作为错误代码来描述错误(很失败的一种做法,谁知道2342是什么意思呢?),现在使用命名的异常类来描述(例如:SecurityException)。
2.异常类彼此之间的关系并没有紧密的联系在一起。相反的,用来描述某一类错误的整数代码在整个错误描述代码中必须是唯一。
3. 在ReadSource方法中没有没有抛出详细说明。在C#中抛出说明并不是必须的。
然而,最值得注意是:比较两段代码,ReadSource变得非常的清晰、简单、明了。它现在仅包含需要实现其基本功能的语句,没有表现出明显的错误处理。这是可以的,因为如果出现异常,调用堆栈就会自我展开。这个版本是我们想要的“理想”版本。
然而,异常允许我们接近这个ReadSource的理想版本,同时,又阻止我们到达它。ReadSource是一个编码例子,它请求资源(一个TextReader),使用了资源(Read),并且释放了资源(Close)。问题是如果在请求资源的过程中出现了异常,那么资源将不会被释放。这个问题的解决是本文的一部分。不过,“理想“中的ReadSource版本仍然是有用的。我们将使用它做为下面几个版本的ReadSource评价的参照物。

三、finally?
    解决释放问题的方法依靠你现在使用的语言。在C++中,你可以使用构建于堆栈上的析构函数。Java中,你能构使用finally程序块。C#允许你创造自定义的结构类型但是不允许结构中的析构函数(只是因为一个C#析构函数其实是一个Finally方法,Finally被垃圾回收器调用。结构类,是一种值类型,并不归属于垃圾回收器回收的范围)。因而,只是在开始,C#必须追循Java的道路,使用finally程序块。首先,我们的finally程序块开起来如下:
None.gif private   static   char [] ReadSource( string  filename)
ExpandedBlockStart.gifContractedBlock.gif 
dot.gif {
InBlock.gif  
try
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   FileInfo file 
= new FileInfo(filename);
InBlock.gif   
int length = (int)file.Length;
InBlock.gif   
char[] source = new char[length];
InBlock.gif   TextReader reader 
= file.OpenText();
InBlock.gif   reader.Read(source, 
0, length);
ExpandedSubBlockEnd.gif  }

InBlock.gif  
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   reader.Close();
ExpandedSubBlockEnd.gif  }

InBlock.gif  
return source;
ExpandedBlockEnd.gif }

None.gif

这个版本不得不引入一个try程序块(既然一个finally程序快必须跟随在一个try程序块后),这将是一个合理的解决方案,如果它奏效的话。但是,它没有做到。问题是try程序块构建成一个范围,所以在finally程序块中的reader并不在这个范围内并且返回语句中的source也不在这个范围。
  
   finally?
   为了解决这个问题,你不得不将reader和source的声明移到try程序块的外面,第二次尝试如下:
None.gif private   static   char [] ReadSource( string  filename)
ExpandedBlockStart.gifContractedBlock.gif 
dot.gif {
InBlock.gif  TextReader reader;
InBlock.gif  
char[] source;
InBlock.gif  
try
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   FileInfo file 
= new FileInfo(filename);
InBlock.gif   
int length = (int)file.Length;
InBlock.gif   source 
= new char[length];
InBlock.gif   reader 
= file.OpenText();
InBlock.gif   reader.Read(source, 
0, length);
ExpandedSubBlockEnd.gif  }

InBlock.gif  
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   reader.Close();
ExpandedSubBlockEnd.gif  }

InBlock.gif  
return source;
ExpandedBlockEnd.gif }

None.gif

这个版本将reader和source的声明移到了try程序块的外面,接着指派给reader和source但没有初始化它们。这是和开始的“理想”版本不同的另外一个地方(出现两个多余行)。然而,你可能认为如果它工作,那将是一个合理的解决方案。但是它没有。问题是委派并不等同于初始化及让编译器知道它。如果在reader被分配之前出现一个异常,这是在finally程序块中对reader.close()的调用
将根据没有被分配的reader,C#,像Java一样,不允许那样。

    finally?
    很明显,你必须要初始化reader,第三次尝试如下:

None.gif private   static   char [] ReadSource( string  filename)
ExpandedBlockStart.gifContractedBlock.gif 
dot.gif {
InBlock.gif  TextReader reader 
= null;
InBlock.gif  
char[] source;
InBlock.gif  
try
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   FileInfo file 
= new FileInfo(filename);
InBlock.gif   
int length = (int)file.Length;
InBlock.gif   source 
= new char[length];
InBlock.gif   reader 
= file.OpenText();
InBlock.gif   reader.Read(source, 
0, length);
ExpandedSubBlockEnd.gif  }

InBlock.gif  
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   reader.Close();
ExpandedSubBlockEnd.gif  }

InBlock.gif  
return source;
ExpandedBlockEnd.gif }

None.gif
这个版本引入了空值,这没有出现在最初的“理想版本”中。不过,如果你仍然认为如果它起作用这将是一个合理的解决方式,然而它不是(虽然它能通过编译)。问题是你在调用reader.close()的时候很容易抛出NullReferenceException异常。

   finally?
   一种解决方法是对eader.close()方法进行保护,下面做第四次尝试:

None.gif private   static   char [] ReadSource( string  filename)
ExpandedBlockStart.gifContractedBlock.gif 
dot.gif {
InBlock.gif  TextReader reader 
= null;
InBlock.gif  
char[] source;
InBlock.gif  
try
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   FileInfo file 
= new FileInfo(filename);
InBlock.gif   
int length = (int)file.Length;
InBlock.gif   source 
= new char[length];
InBlock.gif   reader 
= file.OpenText();
InBlock.gif   reader.Read(source, 
0, length);
ExpandedSubBlockEnd.gif  }

InBlock.gif  
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   
if (reader != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif   
dot.gif{
InBlock.gif    reader.Close();
ExpandedSubBlockEnd.gif   }

ExpandedSubBlockEnd.gif  }

InBlock.gif  
return source;
ExpandedBlockEnd.gif }

None.gif


当然,对reader.close()的保护并不是ReadSource的理想版本。但是,如果仅从它的效果上看,这将是一个合理的版本。最终,工作。它和最初的版本已经大不相同。稍微努力,你能复用下面这段代码:

 

None.gif private   static   char [] ReadSource( string  filename)
ExpandedBlockStart.gifContractedBlock.gif 
dot.gif {
InBlock.gif  FileInfo file 
= new FileInfo(filename);
InBlock.gif  
int length = (int)file.Length;
InBlock.gif  
char[] source = new char[length];
InBlock.gif  TextReader reader 
= file.OpenText();
InBlock.gif  
try
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   reader.Read(source, 
0, length);
ExpandedSubBlockEnd.gif  }

InBlock.gif  
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif   
if (reader != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif   
dot.gif{
InBlock.gif    reader.Close();
ExpandedSubBlockEnd.gif   }

ExpandedSubBlockEnd.gif  }

InBlock.gif  
return source;
ExpandedBlockEnd.gif }

None.gif

在某些情况下,你可以在finally程序块中对可能出现空值进行判断(上面就是一个这样的例子),但是一般说来,你最好还是在finally程序块中判断一下(考虑到如果file.OpenText返回空值,或者如果reader被放进了try程序块中,或者reader作为ref/out参数被传递到try程序块中)。你不得不增加一个try程序块,一个finally程序块,和一个if防护。如果你正在使用Java,你不得不每一次都去做这些事情。在那里是个最大的问题。如果这个解决方案非常令人厌烦且完全偏离于最初的“完美”方案,这没有关系,你能够将这些异常提取到一块。在Java中,你不能这么做。遇到异常,Java将停止运行,而C#则将继续。
 

转载于:https://www.cnblogs.com/sopper/archive/2007/01/15/620637.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值