CLR via C# 笔记 -- 异常和状态管理(20)
1. 异常是指成员没有完成它的名称所宣称的行动,异常类继承System.Exception。
2. .Net Framework 异常处理机制是用Microsoft windows提供的结构化异常处理(Structured Exception Handing, SEH)机制构建的。
3. AppDomain的FirstChanceException事件登记,只要AppDomain发生异常,就会收到通知,通知是在CLR开始搜索任何catch快之前发生的。
4. 由于其他语言不一定要继承Exception,CLR 2.0引入System.Runtime.ComplierServices.RuntimeWrappedException 对其他语言进行包装。
5. System.Exception属性
1) Message:包含辅助性文字说明,指出抛出异常的原因。如果抛出的异常未处理,该消息通常被写入日志。由于最终用户一般不看这种消息,所以消息应提供尽可能多的技术细节。
2) Data:键值对集合,代码抛出异常前在该集合中添加记录项;捕捉异常的代码可在异常恢复过程中查询记录项并利用其中信息。
3) Source:包含生成异常的程序集的名称。
4) StackTrace:包含抛出异常之前调用过的所有方法的名称和签名,该属性对调试很有用。
5) InnerException:如果当前异常是在处理一个异常时抛出的。该属性就指出上一个异常是什么。这个只读属性通常为null。Exception类型还提供了公共方法GetBaseException来遍历由内层异常构成的链表
6. 重新抛出异常
try { ... } catch (Exception e) { ... throw e; // CLR 认为这是异常的起点 } try { ... } catch (Exception e) { ... throw; // 不影响CLR对异常起点的认知。FxCop不再报错 }
7. 如果能找到你的程序集的调用符合(存储在.pdb文件中),那么在System.Exception的StackTrace属性或者System.Diagnostics.StackTrace的Tostring()方法返回的字符串中,将包括源代码文件路径和代码行号,这些信息对于调试是很有用的。
8. [assembly: System.Diagnostics.Debuggable(System.Diagnostics.DebuggableAttribute.DebuggingModes.DisableOptimizations)] JIT编译器不会对程序集的方法进行内联。
9. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 禁止JIT编译器在调试和发布生成(debug and release build)时对该方法进行内联处理。
10. 定义异常类 1). 尽量创建少的基类。原因是基类的主要作用就是将大量错误当作一个错误,而这通常是危险的。基于同样的考虑,永远都不要抛出一个System.Exception对象,抛出其他任何基类异常类型时也要特别谨慎。
class Program { static void Main(string[] args) { try { throw new Exception<DiskFullExceptionArgs>(new DiskFullExceptionArgs("C:/"), "The disk is full"); } catch (Exception<DiskFullExceptionArgs> e) { Console.WriteLine(e.Message); } } } [Serializable] public sealed class Exception<TExceptionArgs> : Exception, ISerializable where TExceptionArgs : ExceptionArgs { private const string c_args = "Args"; // 用于(反)序列化 private readonly TExceptionArgs m_args; public TExceptionArgs Args { get { return m_args; } } public Exception(string message = null, Exception innerException = null) :this(null, message, innerException) { } public Exception(TExceptionArgs args, string msssage = null, Exception innerException = null) :base (msssage, innerException) { m_args = args; } // 这个构造器用于反序列化;由于类是密封的,所以构造器是私有的 // 如果这个类不是密封的,这个构造器就应该是受保护的 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] private Exception(SerializationInfo info, StreamingContext context) : base(info, context) { m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs)); } // 这个方法用于序列化;由于 ISerializable 接口,所以它是公共的 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(c_args, m_args); base.GetObjectData(info, context); } public override string Message { get { string baseMsg = base.Message; return (m_args == null) ? baseMsg : baseMsg + "(" + m_args.Message + ")"; } } public override bool Equals(object obj) { Exception<TExceptionArgs> other = obj as Exception<TExceptionArgs>; if (other == null) return false; return Object.Equals(m_args, other.m_args) && base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } } [Serializable] public abstract class ExceptionArgs { public virtual string Message { get { return string.Empty; } } } /// <summary> /// 代表磁盘满的异常类 /// </summary> [Serializable] public sealed class DiskFullExceptionArgs : ExceptionArgs { private readonly string m_diskpath; // 在构造时设置的私有字段 public DiskFullExceptionArgs(string diskpath) { m_diskpath = diskpath; } // 返回字段的公共只读属性 public string DiskPath { get { return m_diskpath; } } public override string Message => m_diskpath == null ? base.Message : "DiskPath=" + m_diskpath; }