现在的位置: 首页 > 综合 > 正文

结构化异常处理 笔记

2012年10月04日 ⁄ 综合 ⁄ 共 11103字 ⁄ 字号 评论关闭

Throwing a Generic Exception

When you wish to send the error object back to the caller, make use of the C# throw keyword.

// This time, throw an exception if the user speeds up beyond MaxSpeed.
public void Accelerate(int delta)
{
    if (carIsDead)
        Console.WriteLine("{0} is out of order...", petName);
    else
    {
        currSpeed += delta;
        if (currSpeed >= MaxSpeed)
        {
            carIsDead = true;
            currSpeed = 0;
            // Use the "throw" keyword to raise an exception.
            throw new Exception(string.Format("{0} has overheated!", petName));
        }
        else
            Console.WriteLine("=> CurrSpeed = {0}", currSpeed);
    }
}

引发普通的异常

使用C#中的throw关键字将错误对象返回给调用者。

// 这次如果用户加速到超过最大速度,就会引发异常.
public void Accelerate(int delta)
{
    if (carIsDead)
        Console.WriteLine("{0} is out of order...", petName);
    else
    {
        currSpeed += delta;
        if (currSpeed >= MaxSpeed)
        {
            carIsDead = true;
            currSpeed = 0;
            // 使用“throw”关键字引发异常.
            throw new Exception(string.Format("{0} has overheated!", petName));
        }
        else
            Console.WriteLine("=> CurrSpeed = {0}", currSpeed);
    }
}

The TargetSite Property

static void Main(string[] args)
{
    ...
    // TargetSite actually returns a MethodBase object.
    catch(Exception e)
    {
        Console.WriteLine("\n*** Error! ***");
        Console.WriteLine("Member name: {0}", e.TargetSite);
        Console.WriteLine("Class defining member: {0}",
        e.TargetSite.DeclaringType);
        Console.WriteLine("Member type: {0}", e.TargetSite.MemberType);
        Console.WriteLine("Message: {0}", e.Message);
        Console.WriteLine("Source: {0}", e.Source);
    }
    Console.WriteLine("\n***** Out of exception logic *****");
    Console.ReadLine();
}

TargetSite属性

static void Main(string[] args)
{
    ...
    // TargetSite实际上返回一个MethodBase对象.
    catch(Exception e)
    {
        Console.WriteLine("\n*** Error! ***");
        Console.WriteLine("Member name: {0}", e.TargetSite);
        Console.WriteLine("Class defining member: {0}",
        e.TargetSite.DeclaringType);
        Console.WriteLine("Member type: {0}", e.TargetSite.MemberType);
        Console.WriteLine("Message: {0}", e.Message);
        Console.WriteLine("Source: {0}", e.Source);
    }
    Console.WriteLine("\n***** Out of exception logic *****");
    Console.ReadLine();
}

The StackTrace Property

catch(Exception e)
{
    ...
    Console.WriteLine("Stack: {0}", e.StackTrace);
}

StackTrace属性

catch(Exception e)
{
    ...
    Console.WriteLine("Stack: {0}", e.StackTrace);
}

 

The HelpLink Property and The Data Property

public void Accelerate(int delta)
{
    if (carIsDead)
        Console.WriteLine("{0} is out of order...", petName);
    else
    {
        currSpeed += delta;
        if (currSpeed >= MaxSpeed)
        {
            carIsDead = true;
            currSpeed = 0;
            // We need to call the HelpLink property, thus we need
            // to create a local variable before throwing the Exception object.
            Exception ex =
            new Exception(string.Format("{0} has overheated!", petName));
            ex.HelpLink = "http://www.CarsRUs.com";
            // Stuff in custom data regarding the error.
            ex.Data.Add("TimeStamp",
            string.Format("The car exploded at {0}", DateTime.Now));
            ex.Data.Add("Cause", "You have a lead foot.");
            throw ex;
        }
        else
            Console.WriteLine("=> CurrSpeed = {0}", currSpeed);
    }
}

HelpLink属性和Data属性

public void Accelerate(int delta)
{
    if (carIsDead)
        Console.WriteLine("{0} is out of order...", petName);
    else
    {
        currSpeed += delta;
        if (currSpeed >= MaxSpeed)
        {
            carIsDead = true;
            currSpeed = 0;
            // 我们需要调用HelpLink属性,因此需要在异常对象引发之前先创建一个局部变量.
            Exception ex =
            new Exception(string.Format("{0} has overheated!", petName));
            ex.HelpLink = "http://www.CarsRUs.com";
            // 填充关于错误的自定义数据.
            ex.Data.Add("TimeStamp",
            string.Format("The car exploded at {0}", DateTime.Now));
            ex.Data.Add("Cause", "You have a lead foot.");
            throw ex;
        }
        else
            Console.WriteLine("=> CurrSpeed = {0}", currSpeed);
    }
}

 

catch (Exception e)
{
    ...
    // By default, the data field is empty, so check for null.
    Console.WriteLine("\n-> Custom Data:");
    if (e.Data != null)
    {
        foreach (DictionaryEntry de in e.Data)
        Console.WriteLine("-> {0}: {1}", de.Key, de.Value);
    }
}
catch (Exception e)
{
    ...
    // 默认情况下,data字段是空的,需要检查它是否为空.
    Console.WriteLine("\n-> Custom Data:");
    if (e.Data != null)
    {
        foreach (DictionaryEntry de in e.Data)
        Console.WriteLine("-> {0}: {1}", de.Key, de.Value);
    }
}

Exceptions that are thrown by the .NET platform are (appropriately) called system exceptions.

These exceptions are regarded as nonrecoverable, fatal errors. Such as ArgumentOutOfRangeException, IndexOutOfRangeException, StackOverflowException, and so forth.

准确的说,.NET平台引发的异常应被称为系统异常。这些异常被认为是无法修复的致命错误。比如ArgumentOutOfRangeException, IndexOutOfRangeException, StackOverflowException等。

 

Best practice dictates that you derive your custom exceptions from the System.ApplicationException type:

public class ApplicationException : Exception
{
    //  Various constructors.
}

自定义异常应当派生自System.ApplicationException类型:

public class ApplicationException : Exception
{
    //  各种构造函数.
}

Building Custom Exceptions,Take One

The first step is to derive a new class from System.ApplicationException (by convention, all exception classes end with the “Exception” suffix; in fact, this is a .NET best practice).

Note As a rule, all custom exception classes should be defined as public types (recall, the default access modifier of a non-nested type is internal). The reason is that exceptions are often passed outside of assembly boundaries, and should therefore be accessible to the calling code base.

public class CarIsDeadException : ApplicationException
{
    private string messageDetails;
    private DateTime errorTimeStamp;
    private string causeOfError;
    public DateTime TimeStamp
    {
        get {return errorTimeStamp;}
        set {errorTimeStamp = value;}
    }
    public string Cause
    {
        get {return causeOfError;}
        set {causeOfError = value;}
    }
    public CarIsDeadException(){}
    public CarIsDeadException(string message,
    string cause, DateTime time)
    {
        messageDetails = message;
        causeOfError = cause;
        errorTimeStamp = time;
    }
    // Override the Exception.Message property.
    public override string Message
    {
        get
        {
            return string.Format("Car Error Message: {0}", messageDetails);
        }
    }
}

构建自定义异常,第一部分

第一步就是创建一个派生自System.ApplicationException的新类(按照约定,所有的异常类均应以“Exception”后缀结束,这是.NET的最佳实践)。

说明  作为一个规则,所有自定义异常类都应该定义为公共类型(回忆一下,非嵌套类型的默认访问修饰符都是内部的)。这是因为异常通常都会跨程序集边界进行传递,也应该可以被调用代码所访问。

public class CarIsDeadException : ApplicationException
{
    private string messageDetails;
    private DateTime errorTimeStamp;
    private string causeOfError;
    public DateTime TimeStamp
    {
        get {return errorTimeStamp;}
        set {errorTimeStamp = value;}
    }
    public string Cause
    {
        get {return causeOfError;}
        set {causeOfError = value;}
    }
    public CarIsDeadException(){}
    public CarIsDeadException(string message,
    string cause, DateTime time)
    {
        messageDetails = message;
        causeOfError = cause;
        errorTimeStamp = time;
    }
    // 重写Exception.Message属性.
    public override string Message
    {
        get
        {
            return string.Format("Car Error Message: {0}", messageDetails);
        }
    }
}

Throwing this error

// Throw the custom CarIsDeadException.
public void Accelerate(int delta)
{
    ...
    CarIsDeadException  ex =
    new CarIsDeadException (string.Format("{0} has overheated!", petName),
    "You have a lead foot", DateTime.Now);
    ex.HelpLink = "http://www.CarsRUs.com";
    throw ex;
    ...
}

引发异常

// 引发自定义CarIsDeadException.
public void Accelerate(int delta)
{
    ...
    CarIsDeadException  ex =
    new CarIsDeadException (string.Format("{0} has overheated!", petName),
    "You have a lead foot", DateTime.Now);
    ex.HelpLink = "http://www.CarsRUs.com";
    throw ex;
    ...
}

catch this incoming exception

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Custom Exceptions *****\n");
    Car myCar = new Car("Rusty", 90);
    try
    {
        // Trip exception.
        myCar.Accelerate(50);
    }
    catch (CarIsDeadException e)
    {
        Console.WriteLine(e.Message);
        Console.WriteLine(e.TimeStamp);
        Console.WriteLine(e.Cause);
    }
    Console.ReadLine();
}

捕获异常

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Custom Exceptions *****\n");
    Car myCar = new Car("Rusty", 90);
    try
    {
        // 行程异常.
        myCar.Accelerate(50);
    }
    catch (CarIsDeadException e)
    {
        Console.WriteLine(e.Message);
        Console.WriteLine(e.TimeStamp);
        Console.WriteLine(e.Cause);
    }
    Console.ReadLine();
}

Building Custom Exceptions,Take Two

The current CarIsDeadException type has overridden the System.Exception.Message property in order to configure a custom error message and supplied two custom properties to account for additional bits of data. In reality, however, we are not required to override the virtual Message property, as we could simply pass the incoming message to our parent’s constructor as follows:

public class CarIsDeadException : ApplicationException
{
    private DateTime errorTimeStamp;
    private string causeOfError;
    public DateTime TimeStamp
    {
        get { return errorTimeStamp; }
        set { errorTimeStamp = value; }
    }
    public string Cause
    {
        get { return causeOfError; }
        set { causeOfError = value; }
    }
    public CarIsDeadException() { }
    // Feed message to parent constructor.
    public CarIsDeadException(string message,
    string cause, DateTime time)
        : base(message)
    {
        causeOfError = cause;
        errorTimeStamp = time;
    }
}

构建自定义异常,第二部分

为了配置自定义错误信息,当前的CarIsDeadException类型重写了System.Exception.Message虚属性,并提供两个自定义属性来说明其他数据。事实上,不用重写Message虚属性,而只需要将传入的信息按以下方式传递给父对象的构造函数:

public class CarIsDeadException : ApplicationException
{
    private DateTime errorTimeStamp;
    private string causeOfError;
    public DateTime TimeStamp
    {
        get { return errorTimeStamp; }
        set { errorTimeStamp = value; }
    }
    public string Cause
    {
        get { return causeOfError; }
        set { causeOfError = value; }
    }
    public CarIsDeadException() { }
    // 将信息传递给父对象构造函数.
    public CarIsDeadException(string message,
    string cause, DateTime time)
        : base(message)
    {
        causeOfError = cause;
        errorTimeStamp = time;
    }
}

Many times, the role of a custom exception is not necessarily to provide additional functionality beyond what is inherited from the base classes, but to provide a strongly named type that clearly identifies the nature of the error.

很多情况下,自定义异常类型类的作用并不是提供继承基类之外附加的功能,而是提供明确标识错误种类的强命名类型。

 

Building Custom Exceptions,Take Three

If you wish to build a truly prim-and-proper custom exception class, you would want to make sure your type adheres to the exception-centric .NET best practices. Specifically, this requires that your custom exception

• Derives from Exception/ApplicationException

• Is marked with the [System.Serializable] attribute

• Defines a default constructor

• Defines a constructor that sets the inherited Message property

• Defines a constructor to handle “inner exceptions”

• Defines a constructor to handle the serialization of your type

[Serializable]
public class CarIsDeadException : ApplicationException
{
    public CarIsDeadException() { }
    public CarIsDeadException(string message) : base(message) { }
    public CarIsDeadException(string message,
    System.Exception inner)
        : base(message, inner) { }
    protected CarIsDeadException(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
    // Any additional custom properties, constructors and data members...
}

构建自定义异常,第三部分

如果你想构造一个真正意义上严谨规范的自定义异常类,需要确保遵守.NET异常处理的最佳实践。具体来讲,自定义异常需要:

  • 继承自Exception/ApplicationException类;
  • 有[System.Serializable]特性标记;
  • 定义一个默认的构造函数;
  • 定义一个设定继承的Message属性的构造函数;
  • 定义一个处理“内部异常”的构造函数;
  • 定义一个处理类型序列化的构造函数。
[Serializable]
public class CarIsDeadException : ApplicationException
{
    public CarIsDeadException() { }
    public CarIsDeadException(string message) : base(message) { }
    public CarIsDeadException(string message,
    System.Exception inner)
        : base(message, inner) { }
    protected CarIsDeadException(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
    // 其他自定义属性、构造函数、数据成员...
}

You must be aware that when an exception is thrown, it will be processed by the “first available” catch.

必须注意一个异常引发后都将被第一个可用的catch处理。

 

Inner Exceptions

When you encounter an exception while processing another exception, best practice states that you should record the new exception object as an “inner exception” within a new object of the same type as the initial exception (that was a mouthful). The reason we need to allocate a new object of the exception being handled is that the only way to document an inner exception is via a constructor parameter. Consider the following code:

catch (CarIsDeadException e)
{
    try
    {
        FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
        ...
    }
    catch (Exception e2)
    {
        // Throw an exception that records the new exception,
        // as well as the message of the first exception.
        throw new CarIsDeadException(e.Message, e2);
    }
}

内部异常

如果在处理一个异常的时候遇到另一个异常,最好的习惯是将这个新异常对象标识为与第一个异常类型相同的新对象中的“内部错误”,这个建议比较拗口。我们之所以需要创建一个异常的新对象来等待处理,是因为声明一个内部错误的唯一途径就是将其作为一个构造函数参数。

catch (CarIsDeadException e)
{
    try
    {
        FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
        ...
    }
    catch (Exception e2)
    {
        // 引发记录新异常的异常,还有第一个异常的相关信息.
        throw new CarIsDeadException(e.Message, e2);
    }
}

 

 

抱歉!评论已关闭.