Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 未附加调试器时捕获异常 期望的行为(问题)_C#_Vb.net_Visual Studio_Debugging - Fatal编程技术网

C# 未附加调试器时捕获异常 期望的行为(问题)

C# 未附加调试器时捕获异常 期望的行为(问题),c#,vb.net,visual-studio,debugging,C#,Vb.net,Visual Studio,Debugging,在C#应用程序中,我想要的是: 未附加调试器时:- 异常被抛出 异常被捕获到堆栈的更高位置 记录错误并继续 附加调试器时:- 异常被抛出 调试器在引发异常的点处中断 通过一个例子来说明,下面是它如何与条件catch一起工作(我知道C#不支持这一点): 注意:当我展示代码引发异常的示例时,它可能是由第三方库引发的 static void DoSomething() { //This is where I would like the debugger to break execution

在C#应用程序中,我想要的是:

未附加调试器时:-

  • 异常被抛出
  • 异常被捕获到堆栈的更高位置
  • 记录错误并继续 附加调试器时:-

  • 异常被抛出
  • 调试器在引发异常的点处中断 通过一个例子来说明,下面是它如何与条件catch一起工作(我知道C#不支持这一点):

    注意:当我展示代码引发异常的示例时,它可能是由第三方库引发的

    static void DoSomething()
    {
        //This is where I would like the debugger to break execution and show the exception
        throw new Exception( "Something went wrong!" );
    }  
    
    static public void DoSomeStep()
    {
        try
        {
            DoSomething();
        }
        catch( Exception exception when System.Diagnostics.Debugger.IsAttached == false ) //If the debugger is attached don't catch                
        {
            Console.WriteLine( exception.Message ); //Do some processing on the exception                
        }
    }
    static void Main( string[] args )
    {
        for( int i = 0; i < 10; i++ )
        {
            DoSomeStep();
        }
    }
    
    注意,
    Run
    需要有不同的重载,这取决于参数的数量(在本例中,我恰好使用了4个参数)。此外,对于需要在被调用的方法和错误处理程序之间维护某些状态的情况,还有一个
    Context
    参数

    然后,我的代码如下所示:

    static bool DoSomething( int a, int b, int c, int d, RunContext context )
    {
        //Now the debugger break at this point - hooray!
        throw new Exception( "Something went wrong!" );
        return true;
    }
    
    static void HandleException( Exception exception, RunContext context )
    {
        //Only see this when not attached in the debugger
        Console.WriteLine( exception.Message ); //Do some processing on the exception                            
    }
    
    class RunContext{ } //context information - not used in this example
    
    static public void DoSomeStep()
    {
        DebuggerNoCatch.Run<int, int, int, int, RunContext>( DoSomething, 1, 1, 1, 1, new RunContext(), HandleException );
    }
    
    问题是,虽然
    throw
    保留堆栈跟踪,但调试器在发生throw的行而不是原始throw处中断。这样做完全有道理,但这不是我想要的。这意味着我需要在异常中查找stacktrace,然后找到正确的代码行。此外,发生异常的局部变量的状态也将丢失

    包装方法 基本上,只需用单独的方法包装
    try…catch

        static void DoSomething()
        {
            //This is where I would like the debugger to break execution and show the exception
            throw new Exception( "Something went wrong!" );
        }
        static void DoSomethingContinueOnError()
        {
            try
            {
                DoSomething();
            }
            catch( Exception exception )
            {
                Console.WriteLine( exception.Message ); //Do some processing on the exception
            }
        }
        static public void DoSomeStep()
        {
            if( System.Diagnostics.Debugger.IsAttached == false )
            {
                DoSomethingContinueOnError();
            }
            else
            {
                DoSomething();                
            }            
        }        
    
    但是,这方面存在一些问题:

    • 更多代码
    • 对于更复杂的情况,事情很快就会变得难以处理,例如当有更多的参数或
      try…catch
      的局部变量由设置时,如果有子步骤,则需要通过引用传递到“DoSomething”
    条件编译符号 这可能是我最不喜欢的选择。在这种情况下,将使用条件编译符号,例如调试(注意调试将不起作用,因为我可能在未连接编译器的情况下运行调试):

    问题是:-

    • 这是一个有点头痛的管理,我总是不会有它设置时,我需要它。具体地说,除了我手动设置符号定义之外,符号与附加调试器的事实没有任何关系
    • #调试
      会使代码变得混乱,并使
      try…catch
      的可读性降低
    其他
    • VisualStudio设置。我还研究了不同的VisualStudio异常中断设置,但我想为代码的特定部分打开行为,而不是为特定的异常打开行为。此外,这应该适用于所有安装
    • 汇编IL。我已经考虑将内联IL作为生成条件异常的选项,但这需要使用第三方工具进行后期构建步骤
    • 我不认为全局(应用程序)异常处理程序会这样做,因为需要捕获异常并将其记录在应用程序堆栈的较低位置
    更新-DebuggerStepThrough并重新抛出 Steven Liekens的评论指出了似乎是一个好的解决方案,
    调试程序逐步通过属性
    。在包含重新抛出的方法上设置此属性时,调试器将在异常的原始点中断,而不是在重新抛出异常的位置中断,如下所示:

    static bool DoSomething()
    {
         //This is where the debugger now breaks execution
         throw new Exception( "Something went wrong!" );
         return true;
    }
    
    [DebuggerStepThrough]
    static public void DoSomeStep()
    {
        try
        {                
            DoSomething();
        }
        catch( Exception exception )
        {
            Console.WriteLine( exception.Message );
            if( Debugger.IsAttached == true )
            {
                //the debugger no longer breaks here
                throw;
            }
        }
    }
    static void Main( string[] args )
    {          
        for( int i = 0; i < 10; i++ )
        {
            DoSomeStep();
        }
    }
    
    static bool DoSomething()
    {
         //This is where the debugger now breaks execution
         throw new Exception( "Something went wrong!" );
         return true;
    }
    
    [DebuggerStepThrough]
    static public void DoSomeStep()
    {
        try
        {                
            DoSomething();
        }
        catch( Exception exception )
        {
            Console.WriteLine( exception.Message );
            if( Debugger.IsAttached == true )
            {
                //the debugger no longer breaks here
                throw;
            }
        }
    }
    static void Main( string[] args )
    {          
        for( int i = 0; i < 10; i++ )
        {
            DoSomeStep();
        }
    }
    
    static bool DoSomething()
    {
    //这就是调试器现在中断执行的地方
    抛出新异常(“出错了!”);
    返回true;
    }
    [调试步骤至]
    静态公共void DoSomeStep()
    {
    尝试
    {                
    DoSomething();
    }
    捕获(异常)
    {
    Console.WriteLine(异常消息);
    if(Debugger.IsAttached==true)
    {
    //调试器不再在此中断
    投掷;
    }
    }
    }
    静态void Main(字符串[]参数)
    {          
    对于(int i=0;i<10;i++)
    {
    DoSomeStep();
    }
    }
    
    唯一的缺点是,如果您确实希望单步执行标记为
    DebuggerStepThrough
    的代码,或者此代码中存在异常。不过,这是一个小缺点,因为您通常可以将此代码保持最少

    请注意使用
    Debugger.IsAttached
    ,因为我认为它在这里的影响最小,出现奇怪海森堡的可能性最小,但请注意不要使用Guillaume在评论中指出的方法,并在适当时使用另一个选项,如配置设置


    除非有更好的方法或者有人对此表示担忧,否则我将使用这种方法。

    您可以包装异常并捕获特定类型的异常,这样在调试时,没有为异常定义捕获行为,并且调试器将在抛出代码时中断

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                NotImplementedMethod();
            }
            catch (NotImplementedException)
            {
                Console.WriteLine("Exception caught");
            }
            Console.Read();
        }
    
        public static void NotImplementedMethod()
        {
            throw DebugException.Wrap(new NotImplementedException());//Breaks here when debugger is attached
        }
    }
    
    public class DebugException : Exception
    {
        public static Exception Wrap(Exception innerException)
        {
            if(Debugger.IsAttached)
            {
                return new DebugException(innerException);
            }
            else
            {
                return innerException;
            }
        }
    
    
        public DebugException(Exception innerException)
            : base("Debug exception", innerException)
        {
        }
    }
    
    DebuggerStepThrough并重新抛出(已接受的答案) 正如注释中指出的,当在包含重新抛出的方法上设置
    DebuggerStepThroughAttribute
    时,调试器会在异常的初始点中断,而不是在重新抛出异常的位置中断,如下所示:

    static bool DoSomething()
    {
         //This is where the debugger now breaks execution
         throw new Exception( "Something went wrong!" );
         return true;
    }
    
    [DebuggerStepThrough]
    static public void DoSomeStep()
    {
        try
        {                
            DoSomething();
        }
        catch( Exception exception )
        {
            Console.WriteLine( exception.Message );
            if( Debugger.IsAttached == true )
            {
                //the debugger no longer breaks here
                throw;
            }
        }
    }
    static void Main( string[] args )
    {          
        for( int i = 0; i < 10; i++ )
        {
            DoSomeStep();
        }
    }
    
    static bool DoSomething()
    {
         //This is where the debugger now breaks execution
         throw new Exception( "Something went wrong!" );
         return true;
    }
    
    [DebuggerStepThrough]
    static public void DoSomeStep()
    {
        try
        {                
            DoSomething();
        }
        catch( Exception exception )
        {
            Console.WriteLine( exception.Message );
            if( Debugger.IsAttached == true )
            {
                //the debugger no longer breaks here
                throw;
            }
        }
    }
    static void Main( string[] args )
    {          
        for( int i = 0; i < 10; i++ )
        {
            DoSomeStep();
        }
    }
    
    在执行
    Catch()
    语句中的任何内容之前,首先计算
    When()
    语句中指定的谓词

    如果运行该示例,您会注意到调试器在导致异常的行上中断,这是由于巧妙地放置了
    [DebuggerStepThrough]
    属性

    源代码

    /// <summary>
    /// Factory. Provides a static method that initializes a new try-catch wrapper.
    /// </summary>
    public static class DangerousOperation
    {
        /// <summary>
        /// Starts a new try-catch block.
        /// </summary>
        /// <param name="action">The 'try' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'try' block.</returns>
        public static TryCatchBlock Try()
        {
            return new TryCatchBlock();
        }
    
        /// <summary>
        /// Starts a new try-catch block.
        /// </summary>
        /// <param name="action">The 'try' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'try' block.</returns>
        public static TryCatchBlock Try(Action action)
        {
            return new TryCatchBlock(action);
        }
    }
    
    /// <summary>
    /// Wraps a 'try' or 'finally' block.
    /// </summary>
    public class TryCatchBlock
    {
    
        private bool finalized;
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock"/> class;
        /// </summary>
        public TryCatchBlock()
        {
            this.First = this;
        }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock"/> class;
        /// </summary>
        /// <param name="action">The 'try' or 'finally' block's action.</param>
        public TryCatchBlock(Action action)
            : this()
        {
            this.Action = action;
        }
    
        protected TryCatchBlock(TryCatchBlock antecedent)
        {
            if ( antecedent == null )
            {
                throw new ArgumentNullException("antecedent");
            }
            if ( antecedent.finalized )
            {
                throw new InvalidOperationException("This block has been finalized with a call to 'Finally()'");
            }
            this.First = antecedent.First;
            this.Antecedent = antecedent;
            antecedent.Subsequent = this;
        }
    
        protected TryCatchBlock(TryCatchBlock antecedent, Action action)
            : this(antecedent)
        {
            this.Action = action;
        }
    
        public Action Action { get; set; }
    
        /// <summary>
        /// Gets the 'try' block.
        /// </summary>
        public TryCatchBlock First { get; private set; }
    
        /// <summary>
        /// Gets the next block.
        /// </summary>
        public TryCatchBlock Antecedent { get; private set; }
    
        /// <summary>
        /// Gets the previous block.
        /// </summary>
        public TryCatchBlock Subsequent { get; private set; }
    
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<Exception> Catch()
        {
            return new TryCatchBlock<Exception>(this);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<Exception> Catch(Action<Exception> action)
        {
            return new TryCatchBlock<Exception>(this, action);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<TException> Catch<TException>() where TException : System.Exception
        {
            return new TryCatchBlock<TException>(this);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
        /// <param name="action">The 'catch' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<TException> Catch<TException>(Action<TException> action) where TException : System.Exception
        {
            return new TryCatchBlock<TException>(this, action);
        }
    
        /// <summary>
        /// Creates a new 'finally' block and finalizes the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'finally' block.</returns>
        public TryCatchBlock Finally()
        {
            return new TryCatchBlock(this) { finalized = true };
        }
    
        /// <summary>
        /// Creates a new 'finally' block and finalizes the chain.
        /// </summary>
        /// <param name="action">The 'finally' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'finally' block.</returns>
        public TryCatchBlock Finally(Action action)
        {
            return new TryCatchBlock(this, action) { finalized = true };
        }
    
        /// <summary>
        /// Gets a value indicating whether this 'catch' wrapper can handle and should handle the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>Returns <c>true</c> if the exception can be handled; otherwise <c>false</c>.</returns>
        public virtual bool CanHandle(Exception exception)
        {
            return false;
        }
    
        /// <summary>
        /// Handles the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public virtual void Handle(Exception exception)
        {
            throw new InvalidOperationException("This is not a 'catch' block wrapper.");
        }
    
        /// <summary>
        /// Executes the chain of 'try-catch' wrappers.
        /// </summary>
        //[DebuggerStepThrough]
        public void Execute()
        {
            TryCatchBlock current = this.First;
    
            try
            {
                if ( current.Action != null )
                {
                    current.Action();
                }
            }
            catch ( Exception exception )
            {
                while ( current.Subsequent != null )
                {
                    current = current.Subsequent;
    
                    if ( current.CanHandle(exception) )
                    {
                        current.Handle(exception);
                        break;
                    }
    
                    if ( current.Subsequent == null )
                    {
                        throw;
                    }
                }
            }
            finally
            {
                while ( current.Subsequent != null )
                {
                    current = current.Subsequent;
                    if ( current.finalized && current.Action != null )
                    {
                        current.Action();
                    }
                }
            }
        }
    }
    
    /// <summary>
    /// Wraps a 'catch' block.
    /// </summary>
    /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
    public class TryCatchBlock<TException> : TryCatchBlock where TException : System.Exception
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock{TException}"/> class;
        /// </summary>
        /// <param name="antecedent">The 'try' or 'catch' block that preceeds this 'catch' block.</param>
        public TryCatchBlock(TryCatchBlock antecedent)
            : base(antecedent) { }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock{TException}"/> class;
        /// </summary>
        /// <param name="antecedent">The 'try' or 'catch' block that preceeds this 'catch' block.</param>
        /// <param name="action">The 'catch' block's action.</param>
        public TryCatchBlock(TryCatchBlock antecedent, Action<TException> action)
            : base(antecedent)
        {
            this.Action = action;
        }
    
        /// <summary>
        /// Sets a predicate that determines whether this block should handle the exception.
        /// </summary>
        /// <param name="predicate">The method that defines a set of criteria.</param>
        /// <returns>Returns the current instance.</returns>
        public TryCatchBlock<TException> When(Predicate<TException> predicate)
        {
            this.Predicate = predicate;
            return this;
        }
    
        /// <summary>
        /// Gets a value indicating whether this 'catch' wrapper can handle and should handle the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>Returns <c>True</c> if the exception can be handled; otherwise false.</returns>
        public override bool CanHandle(Exception exception)
        {
            if ( exception == null )
            {
                throw new ArgumentNullException("exception");
            }
    
            if ( !typeof(TException).IsAssignableFrom(exception.GetType()) )
            {
                return false;
            }
    
            if ( Predicate == null )
            {
                return true;
            }
    
            return Predicate((TException) exception);
        }
    
        /// <summary>
        /// Handles the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public override void Handle(Exception exception)
        {
            if ( this.Action != null )
            {
                this.Action((TException) exception);
            }
        }
    
        /// <summary>
        /// Gets the exception handler.
        /// </summary>
        public Action<TException> Action { get; private set; }
    
        /// <summary>
        /// Gets the predicate that determines whether this wrapper should handle the exception.
        /// </summary>
        public Predicate<TException> Predicate { get; private set; }
    }
    
    //
    ///工厂。提供一个静态方法,用于初始化新的try-catch包装器。
    /// 
    公共静态类危险操作
    {
    /// 
    ///启动一个新的try-catch块。
    /// 
    ///“try”块的操作。
    ///返回包装“try”块的类的新实例。
    公共静态TryCatchBlock Try(
    
    DangerousOperation
        .Try(() =>
        {
            throw new NotImplementedException();
        })
        .Catch((NotImplementedException exception) =>
        {
            Console.WriteLine(exception.Message);
        }).When(ex => !Debugger.IsAttached)
        .Catch((NotSupportedException exception) =>
        {
            Console.WriteLine("This block is ignored");
        }).When(ex => !Debugger.IsAttached)
        .Catch<InvalidProgramException>() /* specifying a handler is optional */
        .Catch()                          /* In fact, specifying the exception type is also optional */
        .Finally(() =>
        {
            Console.WriteLine("Goodbye");
        }).Execute();
    
    /// <summary>
    /// Factory. Provides a static method that initializes a new try-catch wrapper.
    /// </summary>
    public static class DangerousOperation
    {
        /// <summary>
        /// Starts a new try-catch block.
        /// </summary>
        /// <param name="action">The 'try' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'try' block.</returns>
        public static TryCatchBlock Try()
        {
            return new TryCatchBlock();
        }
    
        /// <summary>
        /// Starts a new try-catch block.
        /// </summary>
        /// <param name="action">The 'try' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'try' block.</returns>
        public static TryCatchBlock Try(Action action)
        {
            return new TryCatchBlock(action);
        }
    }
    
    /// <summary>
    /// Wraps a 'try' or 'finally' block.
    /// </summary>
    public class TryCatchBlock
    {
    
        private bool finalized;
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock"/> class;
        /// </summary>
        public TryCatchBlock()
        {
            this.First = this;
        }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock"/> class;
        /// </summary>
        /// <param name="action">The 'try' or 'finally' block's action.</param>
        public TryCatchBlock(Action action)
            : this()
        {
            this.Action = action;
        }
    
        protected TryCatchBlock(TryCatchBlock antecedent)
        {
            if ( antecedent == null )
            {
                throw new ArgumentNullException("antecedent");
            }
            if ( antecedent.finalized )
            {
                throw new InvalidOperationException("This block has been finalized with a call to 'Finally()'");
            }
            this.First = antecedent.First;
            this.Antecedent = antecedent;
            antecedent.Subsequent = this;
        }
    
        protected TryCatchBlock(TryCatchBlock antecedent, Action action)
            : this(antecedent)
        {
            this.Action = action;
        }
    
        public Action Action { get; set; }
    
        /// <summary>
        /// Gets the 'try' block.
        /// </summary>
        public TryCatchBlock First { get; private set; }
    
        /// <summary>
        /// Gets the next block.
        /// </summary>
        public TryCatchBlock Antecedent { get; private set; }
    
        /// <summary>
        /// Gets the previous block.
        /// </summary>
        public TryCatchBlock Subsequent { get; private set; }
    
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<Exception> Catch()
        {
            return new TryCatchBlock<Exception>(this);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<Exception> Catch(Action<Exception> action)
        {
            return new TryCatchBlock<Exception>(this, action);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<TException> Catch<TException>() where TException : System.Exception
        {
            return new TryCatchBlock<TException>(this);
        }
    
        /// <summary>
        /// Creates a new 'catch' block and adds it to the chain.
        /// </summary>
        /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
        /// <param name="action">The 'catch' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock{TException}"/> class that wraps a 'catch' block.</returns>
        public TryCatchBlock<TException> Catch<TException>(Action<TException> action) where TException : System.Exception
        {
            return new TryCatchBlock<TException>(this, action);
        }
    
        /// <summary>
        /// Creates a new 'finally' block and finalizes the chain.
        /// </summary>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'finally' block.</returns>
        public TryCatchBlock Finally()
        {
            return new TryCatchBlock(this) { finalized = true };
        }
    
        /// <summary>
        /// Creates a new 'finally' block and finalizes the chain.
        /// </summary>
        /// <param name="action">The 'finally' block's action.</param>
        /// <returns>Returns a new instance of the <see cref="TryCatchBlock"/> class that wraps the 'finally' block.</returns>
        public TryCatchBlock Finally(Action action)
        {
            return new TryCatchBlock(this, action) { finalized = true };
        }
    
        /// <summary>
        /// Gets a value indicating whether this 'catch' wrapper can handle and should handle the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>Returns <c>true</c> if the exception can be handled; otherwise <c>false</c>.</returns>
        public virtual bool CanHandle(Exception exception)
        {
            return false;
        }
    
        /// <summary>
        /// Handles the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public virtual void Handle(Exception exception)
        {
            throw new InvalidOperationException("This is not a 'catch' block wrapper.");
        }
    
        /// <summary>
        /// Executes the chain of 'try-catch' wrappers.
        /// </summary>
        //[DebuggerStepThrough]
        public void Execute()
        {
            TryCatchBlock current = this.First;
    
            try
            {
                if ( current.Action != null )
                {
                    current.Action();
                }
            }
            catch ( Exception exception )
            {
                while ( current.Subsequent != null )
                {
                    current = current.Subsequent;
    
                    if ( current.CanHandle(exception) )
                    {
                        current.Handle(exception);
                        break;
                    }
    
                    if ( current.Subsequent == null )
                    {
                        throw;
                    }
                }
            }
            finally
            {
                while ( current.Subsequent != null )
                {
                    current = current.Subsequent;
                    if ( current.finalized && current.Action != null )
                    {
                        current.Action();
                    }
                }
            }
        }
    }
    
    /// <summary>
    /// Wraps a 'catch' block.
    /// </summary>
    /// <typeparam name="TException">The type of the exception that this block will catch.</typeparam>
    public class TryCatchBlock<TException> : TryCatchBlock where TException : System.Exception
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock{TException}"/> class;
        /// </summary>
        /// <param name="antecedent">The 'try' or 'catch' block that preceeds this 'catch' block.</param>
        public TryCatchBlock(TryCatchBlock antecedent)
            : base(antecedent) { }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="TryCatchBlock{TException}"/> class;
        /// </summary>
        /// <param name="antecedent">The 'try' or 'catch' block that preceeds this 'catch' block.</param>
        /// <param name="action">The 'catch' block's action.</param>
        public TryCatchBlock(TryCatchBlock antecedent, Action<TException> action)
            : base(antecedent)
        {
            this.Action = action;
        }
    
        /// <summary>
        /// Sets a predicate that determines whether this block should handle the exception.
        /// </summary>
        /// <param name="predicate">The method that defines a set of criteria.</param>
        /// <returns>Returns the current instance.</returns>
        public TryCatchBlock<TException> When(Predicate<TException> predicate)
        {
            this.Predicate = predicate;
            return this;
        }
    
        /// <summary>
        /// Gets a value indicating whether this 'catch' wrapper can handle and should handle the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>Returns <c>True</c> if the exception can be handled; otherwise false.</returns>
        public override bool CanHandle(Exception exception)
        {
            if ( exception == null )
            {
                throw new ArgumentNullException("exception");
            }
    
            if ( !typeof(TException).IsAssignableFrom(exception.GetType()) )
            {
                return false;
            }
    
            if ( Predicate == null )
            {
                return true;
            }
    
            return Predicate((TException) exception);
        }
    
        /// <summary>
        /// Handles the specified exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public override void Handle(Exception exception)
        {
            if ( this.Action != null )
            {
                this.Action((TException) exception);
            }
        }
    
        /// <summary>
        /// Gets the exception handler.
        /// </summary>
        public Action<TException> Action { get; private set; }
    
        /// <summary>
        /// Gets the predicate that determines whether this wrapper should handle the exception.
        /// </summary>
        public Predicate<TException> Predicate { get; private set; }
    }
    
    try
    {
        DoSomething()
    }
    catch (Exception e) when (!System.Diagnostics.Debugger.IsAttached)
    {
        Console.WriteLine(exception.Message);
    }