定义自己的异常类

  • 设计自己的异常不仅繁琐,还容易出错。主要原因是从Exception派生的所有类型都应该是可序列化的(serializable),使它们能穿越AppDomain边界或者写入日志/数据库。

写一个自己的泛型Exception<TExceptionArgs>类,它像下面这样定义

  1. [Serializable]
  2. public sealed class Exception<TExceptionArgs> : Exception, ISerializable
  3. where TExceptionArgs : ExceptionArgs
  4. {
  5. private const String c_args = "Args"; // For (de)serialization
  6. private readonly TExceptionArgs m_args;
  7. public TExceptionArgs Args
  8. {
  9. get
  10. {
  11. return m_args;
  12. }
  13. }
  14. public Exception(String message = null, Exception innerException = null)
  15. : this(null, message, innerException) { }
  16. public Exception(TExceptionArgs args, String message = null,
  17. Exception innerException = null): base(message, innerException)
  18. {
  19. m_args = args;
  20. }
  21. // This constructor is for deserialization; since the class is sealed, the constructor is
  22. // private. If this class were not sealed, this constructor should be protected
  23. [SecurityPermission(SecurityAction.LinkDemand,
  24. Flags = SecurityPermissionFlag.SerializationFormatter)]
  25. private Exception(SerializationInfo info, StreamingContext context)
  26. : base(info, context)
  27. {
  28. m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs));
  29. }
  30. // This method is for serialization; it’s public because of the ISerializable interface
  31. [SecurityPermission(SecurityAction.LinkDemand,
  32. Flags = SecurityPermissionFlag.SerializationFormatter)]
  33. public override void GetObjectData(SerializationInfo info, StreamingContext context)
  34. {
  35. info.AddValue(c_args, m_args);
  36. base.GetObjectData(info, context);
  37. }
  38. public override String Message
  39. {
  40. get
  41. {
  42. String baseMsg = base.Message;
  43. return (m_args == null) ? baseMsg : baseMsg + " (" + m_args.Message + ")";
  44. }
  45. }
  46. public override Boolean Equals(Object obj)
  47. {
  48. Exception<TExceptionArgs> other = obj as Exception<TExceptionArgs>;
  49. if (other == null) return false;
  50. return Object.Equals(m_args, other.m_args) && base.Equals(obj);
  51. }
  52. public override int GetHashCode()
  53. {
  54. return base.GetHashCode();
  55. }
  56. }

TExceptionArgs约束为的ExceptionArgs基类非常简单,它看起来像下面这样

  1. [Serializable]
  2. public abstract class ExceptionArgs {
  3. public virtual String Message { get { return String.Empty; } }
  4. }

要定义代表磁盘满的异常类,可以像下面这样

  1. [Serializable]
  2. public sealed class DiskFullExceptionArgs : ExceptionArgs {
  3. private readonly String m_diskpath; // private field set at construction time
  4. public DiskFullExceptionArgs(String diskpath) { m_diskpath = diskpath; }
  5. // Public read­only property that returns the field
  6. public String DiskPath { get { return m_diskpath; } }
  7. // Override the Message property to include our field (if set)
  8. public override String Message {
  9. get {
  10. return (m_diskpath == null) ? base.Message : "DiskPath=" + m_diskpath;
  11. }
  12. }
  13. }

现在,可以像下面这样写来抛出并捕捉这样的一个异常

  1. public static void TestException() {
  2. try {
  3. throw new Exception<DiskFullExceptionArgs>(
  4. new DiskFullExceptionArgs(@"C:\"), "The disk is full");
  5. }
  6. catch (Exception<DiskFullExceptionArgs> e) {
  7. Console.WriteLine(e.Message);
  8. }
  9. }