定制异常

  第7章讨论了异常,以及如何使用try…catch…finally块处理它们。我们还论述了几个标准的.NET异常,包括异常的基类System.Exception。在应用程序中,有时也可以从这个基类中派生自己的异常类,并使用它们,而不是使用标准的异常。这样就可以把更具体的信息发送给捕获该异常的代码,让处理异常的捕获代码更有针对性。例如,可以给异常类添加一个新属性,以便访问某些底层信息,这样异常的接收代码就可以做出必要的改变,或者仅给出异常起因的更多信息。

  定义了异常类后,就可以使用Debug|Exception对话框中的Add按钮,把它添加到VS可以识别的异常列表中,然后定义与异常相关的操作,如第7章所述。

  在System名称空间中有两个基本的异常类ApplicationException和SystemException,它们派生于Exception。SystemException用作.NET Framework预定义的异常的基类,ApplicationException由开发人员用于派生自己的异常类。但最近的最佳做法是不从这个类中派生异常,而应使用Exception。ApplicationException类在未来可能会被废弃。

给CardLib添加定制异常

  为了演示定制异常的用法,最好通过升级CardLib项目来说明。目前,如果试图访问索引小于0或大于51的扑克牌,Deck.GetCard()方法目前就会抛出一个标准的.NET异常,但下面改为使用一个定制异常。

  首先需要在BegVCSharp\Chapter13目录中创建一个新的类库项目Ch13CardLib,像以前一样把类从Ch12CardLib中复制过来,并把名称空间改为Ch13CardLib。接着定义该异常。方法是使用在新类文件CardOutOfRange Exception.cs中定义一个新类,这个新类是使用Project|Add Class添加到Ch13CardLib项目中的(这段代码包含在Ch13CardLib\CardOutOfRangeException.cs文件中):

  1. public class CardOutOfRangeException : Exception
  2. {
  3. private Cards deckContents;
  4. public Cards DeckContents
  5. {
  6. get
  7. {
  8. return deckContents;
  9. }
  10. }
  11. public CardOutOfRangeException(Cards sourceDeckContents) : base("There are only 52 cards in the deck.")
  12. {
  13. deckContents = sourceDeckContents;
  14. }
  15. }

  这个类的构造函数需要使用Cards类的一个实例,它允许通过DeckContents属性来访问这个Cards对象,为Exception基类构造函数提供合适的错误信息,使该错误信息可以通过类的Message属性得到。

  接着Deck.cs中添加抛出该异常的代码,替换原来的标准异常(这段代码包含在Ch13CardLib\Deck.cs文件中):

  1. public Card GetCard(int cardNum)
  2. {
  3. if(cardNum >= 0 && cardNum <= 51)
  4. return cards[cardNum];
  5. else
  6. throw new CardOutOfRangeException(cards.Clone() as Cards);
  7. }

  DeckContents属性是通过对Deck对象的当前内容(其形式是一个Cards对象)进行深度复制来初始化的。这表示,此时的内容是异常抛出时的内容,所以随后对Deck内容的修改不会丢失这些信息。

  要进行测试,使用下面的客户代码(这段代码包含在Ch13CardClient\Program.cs文件中):

  1. Deck deck1 = new Deck();
  2. try
  3. {
  4. Card myCard = deck1.GetCard(60);
  5. }
  6. catch(CardOutOfRangeException e)
  7. {
  8. Console.WriteLine(e.Message);
  9. Console.WriteLine(e.DeckContents[0]);
  10. }
  11. Console.ReadKey();

  其中捕获代码把异常的Message属性写到屏幕上。我们还通过DeckContents显示了Cards对象中的第一张牌,以证明可以通过定制的异常对象来访问Cards集合。