异常简介
C#只能抛出与CLS兼容的异常,即从System.Exception继承的异常类型。但CLR允许抛出任何类型的对象。C#为我们提供了一种特殊的catch块来捕获与CLS不兼容的异常:
catch // 这里没有指定异常筛选器
{
// 这里执行恢复代码
....
}
这样的catch块也可捕获任何与CLS兼容的异常。
finally块中的代码执行的是一些资源清理操作,这些清理操作通常是对应的try块中的行为所需要的。
异常是对程序接口隐含假设的一种违反。
在设计一个类型时,我们应该首先假设类型最常见的使用方式,然后设计其接口使之能够很好地处理这种情况。最后再考虑接口带来的隐含假设,并且当任何这些假设被违反时便抛出异常。
系统在搜索处理异常的捕获筛选器时,应用程序的性能会受到一些负面影响。应用程序抛出的异常越少,它运行的速度就越快。
System.Exception类的属性与方法
Message:只读属性,String类型,一段辅助性的消息文本,表示异常抛出的原因。
Source:读/写属性,String类型,产生异常的程序集名称
StackTrace:只读属性,String类型,包含异常所历经的方法的名称和签名。该属性对于调试工作非常有用。
TargetSite:只读属性,MethodBase类型,包含抛出异常的方法。
InnerException:只读属性,Exception类型,如果当前的异常是在处理另一个异常时产生的,那么该属性表示前一个异常。该属性通常为null。
GetBaseException:公有方法,遍历所有内部异常组成的链表,并返回最开始抛出的那个异常。
HResult:读/写属性,Int32类型,受保护属性,仅用于托管代码和非托管COM代码互操作的情况。
定义自己的异常
强烈建议大家抛出一个具体的异常类型,即一个没有其他类继承的异常类型。
我们永远都不应该抛出Exception、ApplicationExcption或SystemException类型。
Exception基类型定义了三个公有构造器:
所有的异常类型都应该是可序列化的,只有这样异常对象才能在跨越应用程序域或机器边界时得到封送处理,并在客户端重新抛出。也只有这样才可将异常对象保存在一个日志文件或数据库中。要使新定义的异常类型成为可序列化类型,我们必须在类型上应用一个[Serializable]定制特性,且如果类型定义了任何新的字段,由于基类System.Exception已经实现了ISerializable接口,我们还必须让其实现ISerializable接口的GetObjectData方法和一个特性的受保护的构造器。
下面代码演示了如何正确定义自己的异常类型(这段代码亦很好的演示了如何定义一个序列化类型):
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace ExceptionSample
{
// 允许类实例被序列化
[Serializable]
sealed class DiskFullException : Exception, ISerializable
{
// 三个公有构造器
public DiskFullException()
: base ()
{ }
public DiskFullException( string message)
: base (message)
{ }
public DiskFullException( string message, Exception innerException)
: base (message, innerException)
{ }
// 私有字段
private string diskpath;
// 只读属性,表示出错路径
public string DiskPath { get { return diskpath; } }
// 重写公有属性Message,将新定义的字段包含在异常的消息文本中
public override string Message
{
get
{
string msg = base .Message;
if (diskpath != null )
msg += Environment.NewLine + " Disk Path: " + diskpath;
return msg;
}
}
// 因为定义了至少一个新字段,所以要定义一个特殊的构造器用作反序列化。
// 由于该类是一个密封类,所以该构造器被定义为private,否则,应该被定义为protected
private DiskFullException(SerializationInfo info, StreamingContext context)
: base (info, context) // 让基类反序列化其内定义的字段
{
diskpath = info.GetString( " DiskPath " );
}
// 因为定义了至少一个新字段,所以要定义序列化方法
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
// 序列化新定义的每个字段
info.AddValue( " DiskPath " , diskpath);
// 让基类序列化其内定义的字段
base .GetObjectData(info, context);
}
// 定义额外的构造器设置新定义的字段
public DiskFullException( string message, string diskpath)
: this (message)
{
this .diskpath = diskpath;
}
public DiskFullException( string message, string diskpath, Exception innerException)
: this (message, innerException)
{
this .diskpath = diskpath;
}
}
class Program
{
static void Main( string [] args)
{
// 构造一个DiskFullException对象,并对其进行序列化
DiskFullException e = new DiskFullException( " The disk volume is full " , @" C:\ " );
FileStream fs = new FileStream( @" Test " , FileMode.Create);
IFormatter f = new BinaryFormatter();
f.Serialize(fs, e);
fs.Close();
// 反序列化DiskFullException对象,并查看它的字段
fs = new FileStream( " Test " , FileMode.Open);
e = (DiskFullException)f.Deserialize(fs);
fs.Close();
Console.WriteLine( " Type: {1}{0}DiskPath: {2}{0}Message: {3} " , Environment.NewLine, e.GetType(), e.DiskPath, e.Message);
Console.ReadKey();
}
}
}