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;
}

 

posted @ 2021-06-03 19:58  Karl_Albright  阅读(65)  评论(0编辑  收藏  举报