Exception Handling in C#

Exception Handling in C#

Level Author
Intermediate Anonymous

What 抯 Wrong with Return Codes?
Most programmers have probably written code that looked like this:
bool success =CallFunction();
if (!success)
{
//process the error
}
This works okay, but every return value has to be checked for an error. If the above
was written as
CallFunction();
any error return would be thrown away. That抯 where bugs come from.
There are many different models for communicating status; some functions
may return an HRESULT , some may return a Boolean value, and others may use
some other mechanism.
In the .NET Runtime world, exceptions are the fundamental method of han-dling
error conditions. Exceptions are nicer than return codes because they can抰
be silently ignored.

Trying and Catching
To deal with exceptions, code needs to be organized a bit differently. The sections
of code that might throw exceptions are placed in a try block, and the code to handle
exceptions in the try block is placed in a catch block. Here抯 an example:

using System;
class Test
{
static int Zero =0;
public static void Main()
{
//watch for exceptions here
try
{
int j =22 /Zero;
}
//exceptions that occur in try are transferred here
catch (Exception e)
{
Console.WriteLine("Exception "+e.Message);
}
Console.WriteLine("After catch");
}
}

The try block encloses an expression that will generate an exception. In this case,
it will generate an exception known as DivideByZeroExceptio . When the division
takes place, the .NET Runtime stops executing code and searches for a try block
surrounding the code in which the exception took place. When it finds a try block,
it then looks for associated catch blocks.
If it finds catch blocks, it picks the best one (more on how it determines which
one is best in a minute), and executes the code within the catch block. The code in
the catch block may process the event or rethrow it.
The example code catches the exception and writes out the message that is
contained within the exception object.
The Exception Hierarchy
All C# exceptions derive from the class named Exception , which is part of the Common
Language Runtime 1 . When an exception occurs, the proper catch block is
determined by matching the type of the exception to the name of the exception
mentioned. A catch block with an exact match wins out over a more general
exception. Returning to the example:
using System;
class Test
{
static int Zero =0;
public static void Main()
{
try
{
int j =22 /Zero;
}
//catch a specific exception
catch (DivideByZeroException e)
{
Console.WriteLine("DivideByZero {0}",e);
}
//catch any remaining exceptions
catch (Exception e)
{
Console.WriteLine("Exception {0}",e);
}
}
}
The catch block that catches the DivideByZeroException is the more specific match,
and is therefore the one that is executed.
This example is a bit more complex:

using System;
class Test
{
static int Zero =0;
static void AFunction()
{
int j = 22 / Zero;
//the following line is never executed.
Console.WriteLine("In AFunction()");
}
public static void Main()
{
try
{
AFunction();
}
catch (DivideByZeroException e)
{
Console.WriteLine("DivideByZero {0}",e);
}
}
}
What happens here?
When the division is executed, an exception is generated. The runtime starts
searching for a try block in AFunction(), but it doesn抰 find one, so it jumps out of
AFunction(), and checks for a try in Main(). It finds one, and then looks for a catch
that matches. The catch block then executes.
Sometimes, there won抰 be any catch clauses that match.
using System;
class Test
{
static int Zero =0;
static void AFunction()
{
try
{
int j =22 /Zero;
}
//this exception doesn't match
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("OutOfRangeException:{0}",e);
}
Console.WriteLine("In AFunction()");
}
public static void Main()
{
try
{
AFunction();
}
//this exception doesn't match
catch (ArgumentException e)
{
Console.WriteLine("ArgumentException {0}",e);
}
}
}
Neither the catch block in AFunction()nor the catch block in Main()matches the
exception that抯 thrown. When this happens, the exception is caught by the "last
chance" exception handler. The action taken by this handler depends on how the
runtime is configured, but it will usually bring up a dialog box containing the excep-tion
information and halt the program.
Passing Exceptions on to the Caller
It抯 sometimes the case that there抯 not much that can be done when an exception
occurs; it really has to be handled by the calling function. There are three basic
ways to deal with this, which are named based on their result in the caller: Caller
Beware, Caller Confuse, and Caller Inform.
Caller Beware
The first way is to merely not catch the exception. This is sometimes the right design
decision, but it could leave the object in an incorrect state, causing problems when the
caller tries to use it later. It may also give insufficient information to the caller.
Caller Confuse
The second way is to catch the exception, do some cleanup, and then rethrow
the exception:
using System;
public class Summer
{
int sum =0;
int count =0;
float average;
public void DoAverage()
{
try
{
average =sum /count;
}
catch (DivideByZeroException e)
{
//do some cleanup here
throw e;
}
}
}
class Test
{
public static void Main()
{
Summer summer =new Summer();
try
{
summer.DoAverage();
}
catch (Exception e)
{
Console.WriteLine("Exception {0}",e);
}
}
}
This is usually the minimal bar for handling exceptions; an object should always
maintain a valid state after an exception.
This is called Caller Confuse because while the object is in a valid state after
the exception occurs, the caller often has little information to go on. In this case,
the exception information says that a DivideByZeroException occurred somewhere
in the called function, without giving any insight into the details of the exception
or how it might be fixed.
Sometimes this is okay if the exception passes back obvious information.
Caller Inform
In Caller Inform, additional information is returned for the user. The caught exception
is wrapped in an exception that has additional information.
using System;
public class Summer
{
int sum =0;
int count =0;
float average;
public void DoAverage()
{
try
{
average =sum /count;
}
catch (DivideByZeroException e)
{
//wrap exception in another one,
//adding additional context.
throw (new DivideByZeroException(
"Count is zero in DoAverage()",e));
}
}
}
public class Test
{
public static void Main()
{
Summer summer =new Summer();
try
{
summer.DoAverage();
}
catch (Exception e)
{
Console.WriteLine("Exception:{0}",e);
}
}
}
When the DivideByZeroException is caught in the DoAverage()function, it is wrapped
in a new exception that gives the user additional information about what caused the
exception. Usually the wrapper exception is the same type as the caught exception,
but this might change depending on the model presented to the caller.
This program generates the following output:
Exception:System.DivideByZeroException:Count is zero in DoAverage()--->
System.DivideByZeroException
at Summer.DoAverage()
at Summer.DoAverage()
at Test.Main()
Ideally, each function that wants to rethrow the exception will wrap it in an excep-tion
with additional contextual information.
User-Defined Exception Classes
One drawback of the last example is that the caller can抰 tell what exception hap-pened
in the call to DoAverage()by looking at the type of the exception. To know
that the exception was because the count was zero, the expression message would
have to be searched for the string is zero ".
That would be pretty bad, since the user wouldn抰 be able to trust that the text
would remain the same in later versions of the class, and the class writer wouldn抰
be able to change the text. In this case, a new exception class can be created.
using System;
public class CountIsZeroException:Exceptio
{
public CountIsZeroException()
{
}
public CountIsZeroException(string message)
:base(message)
{
}
public CountIsZeroException(string message,Exception inner)
:base(message,inner)
{
}
}
public class Summer
{
int sum =0;
int count =0;
float average;
public void DoAverage()
{
if (count ==0)
throw(new CountIsZeroException("Zero count in DoAverage"));
else
average =sum /count;
}
}
class Test
{
public static void Main()
{
Summer summer =new Summer();
try
{
summer.DoAverage();
}
catch (CountIsZeroException e)
{
Console.WriteLine("CountIsZeroException:{0}",e);
}
}
}

DoAverage()now determines whether there would be an exception (whether count
is zero), and if so, creates a CountIsZeroException and throws it.
Finally
Sometimes, when writing a function, there will be some cleanup that needs to be
done before the function completes, such as closing a file. If an exception occurs,
the cleanup could be skipped:
using System;
using System.IO;
class Processor
{
int count;
int sum;
public int average;
void CalculateAverage(int countAdd,int sumAdd)
{
count +=countAdd;
sum +=sumAdd;
average =sum /count;
}
public void ProcessFile()
{
FileStream f =new FileStream("data.txt",FileMode.Open);
try
{
StreamReader t =new StreamReader(f);
string line;
while ((line =t.ReadLine())!=null)
{
int count;
int sum;
count =Int32.FromString(line);
line =t.ReadLine();
sum =Int32.FromString(line);
CalculateAverage(count,sum);
}
f.Close();
}
//always executed before function exit,even if an
//exception was thrown in the try.
finally
{
f.Close();
}
}
}
class Test
{
public static void Main()
{
Processor processor =new Processor();
try
{
processor.ProcessFile();
}
catch (Exception e)
{
Console.WriteLine("Exception:{0}",e);
}
}
}

This example walks through a file, reading a count and sum from a file and using it
to accumulate an average. What happens, however, if the first count read from the
file is a zero?
If this happens, the division in CalculateAverage()will throw a DivideByZero-
Exception , which will interrupt the file-reading loop. If the programmer had
written the function without thinking about exceptions, the call to file.Close()
would have been skipped, and the file would have remained open.
The code inside the finally block is guaranteed to execute before the exit of
the function, whether there is an exception or not. By placing the file.Close()call
in the finally block, the file will always be closed.
Efficiency and Overhead
In languages without garbage collection, adding exception handling is expensive,
since all objects within a function must be tracked to make sure that they are
properly destroyed if an exception is thrown. The required tracking code both
adds execution time and code size to a function.
In C#, however, objects are tracked by the garbage collector rather than the
compiler, so exception handling is very inexpensive to implement and imposes little
runtime overhead on the program when the exceptional case doesn抰 occur.
Design Guidelines
Exceptions should be used to communicate exceptional conditions. Don抰 use them to
communicate events that are expected, such as reaching the end of a file. In the
normal operation of a class, there should be no exceptions thrown.
Conversely, don抰 use return values to communicate information that would
be better contained in an exception.
If there抯 a good predefined exception in the System namespace that describes
the exception condition梠ne that will make sense to the users of the class梪se
that one rather than defining a new exception class, and put specific information
in the message. If the user might want to differentiate one case from others where
that same exception might occur, then that would be a good place for a new excep-tion
class.
Finally, if code catches an exception that it isn抰 going to handle, consider whether
it should wrap that exception with additional information before rethrowing it.

时间: 2024-09-29 03:59:12

Exception Handling in C#的相关文章

Flow control and exception Handling

2) Flow control and exception Handling Objective 1)Write code using if and switch statements and identify legal argument types for these statements. ·    Unreachable statements produce a compile-time error. while (false) { x = 3; } // won't compilefo

Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBl

Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[下] 在上篇中,我详细介绍了如何通过自定义ClientMessageInspector和 ErrorHandler,实现WCF与微软企业库中的Exception Handling Application Block(EHAB)之间的集成.这个方案的基本思路就是:当异常从服务端抛出,利 用EHAB针对某个配置好的异常处理策略进行处理:然后将处理有的异常通过 Servic

我的WCF之旅(10):如何在WCF进行Exception Handling

在任何Application的开发中,对不可预知的异常进行troubleshooting时,异常处理显得尤为重要.对于一般的.NET系统来说,我们简单地借助try/catch可以很容易地实现这一功能.但是对于 一个分布式的环境来说,异常处理就没有那么简单了.按照面向服务的原则,我们把一些可复用的业务逻辑以Service的形式实现,各个Service处于一个自治的环境中,一个Service需要和另一个Service进行交互,只需要获得该Service的描述(Description)就可以了(比如W

Enterprise Library深入解析与灵活应用(3):倘若将Unity、PIAB、Exception Handling引入MVP模式.. .. ..

最近在做一个Smart Client Software Factory的项目.熟悉SCSF或者CAB的都应该很清楚MVP这种设计模式.MVP是MVC的一种变体,View和Mode分别关注于UI的呈现和业务模型,View和Mode完全分离,View通过Presenter实现对业务模型的访问,Presenter"间接"地调用View实现对UI的操作.对于MVP中的异常处理,我们是直接通过Enterprise Library的Exception Handling Application Bl

Ruby Study 10 : Exception Handling

exception 在程序开发中是不可或缺的部分, 程序在使用过程中总会遇到不可预知的问题, 例如可预知的访问文件的程序可能在访问过程中文件被删除或移动等. 但是总会有不可预知的, 不可能在写程序时全部规避掉. 所以就有了exception handling. 在Ruby中exception 是Exception calss或者它的subclass的object. 下面来详细的讲解一下异常时如何捕获exception. 注意Ruby的Exception Handling分为几个层面 , 异常执行

谈谈基于SQL Server 的Exception Handling[上篇]

对于所有的开发人员来说,Exception Handling是我们每天都要面对的事情.对于基于Source Code的Exception Handling,我想大家已经司空见惯了,但是对于Database级别的Exception Handling,就没有那么常见了.在这篇文章中,我将会介绍我对于基于Database编程中Exception Handling的一些粗浅的认识:在编写Stored Procedure时,如何抛出一个可预知的Exception,ADO.NET如何处理从Database抛

Microsoft Visual C++ and Win32 structured exception handling

Introduction In an earlier article [1] I described some performance measurements when using exceptions in various languages on Windows. A couple of people since then have asked me questions about how the windows exception model actually works and how

Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]

在<WCF技术剖析(卷1)>的最后一章,我给出了一个具体的应用WCF的分布式应用实例,我把这个实例命名为PetShop.在这个例子中,我利用WCF的扩展实现了一些设计.架构模式,比如AOP.IoC等.看过本书的读者,一定还记得我还通过WCF扩展实现了于微软企业库(Enterprise Library)异常处理应用块(Exception Handling Application Block:EHAB)的集成.当时由于缺乏相应的背景知识,不可能介绍具体的实现,现在我们可以详细来讲述这是如何实现的.

谈谈基于SQL Server 的Exception Handling

对于所有的开发人员来说,Exception Handling是我们每天都要面对的事情.对于基于Source Code的Exception Handling,我想大家已经司空见惯了,但是对于Database级别的Exception Handling,就没有那么常见了.在这篇文章中,我将会介绍我对于基于Database编程中Exception Handling的一些粗浅的认识:在编写Stored Procedure时,如何抛出一个可预知的Exception,ADO.NET如何处理从Database抛