【问题标题】:C# Do I need TRY/CATCH in order to throw?C# 我需要 TRY/CATCH 才能抛出吗?
【发布时间】:2009-01-10 21:19:50
【问题描述】:

如果我只想在异常发生时将其提高一个级别?

private void TryCatch()
{
   try
   {
       foo();
   }
   catch (Exception ex)
   {
       throw;
   }
}

private void NoTryCatch()
{
   foo();
}

这两种方法不一样吗?

如果在 TryCatch 中发生异常,则会向上抛出一个级别,如果在 NoTryCatch 中发生异常,也会将异常向上抛出一个级别。

这个问题是在使用 ReSharper 后提出的,并注意到它建议删除 try/catch 块,因为它是多余的。

【问题讨论】:

    标签: c# try-catch


    【解决方案1】:

    是的,这些方法几乎 (*) 相同。唯一的区别是在第一个断点很容易。我总是选择第二个,除非我真的需要在那里打破并且只在那里(而不是立即抛出任何那种类型的异常,这很容易)。即使我曾经使用过第一种形式,我也会在提交代码之前将其放回第二种形式。

    (*) JIT 处理它们的方式可能存在一些差异。第一个最终会得到更多的 IL,这将影响内联等的机会。

    编辑:我无法抗拒一些微基准测试。看起来 try/catch/throw 对性能的影响比仅仅禁用内联更严重:

    using System;
    using System.Diagnostics;
    using System.Runtime.CompilerServices;
    
    public class Test
    {
        const int Iterations = 1000000000;
    
        static void Main()
        {
            Stopwatch sw;
    
            sw = Stopwatch.StartNew();
            for (int i=0; i < Iterations; i++)
            {
                SimpleMethod();
            }
            sw.Stop();
            Console.WriteLine("Simple method: {0}", sw.ElapsedMilliseconds);
    
            sw = Stopwatch.StartNew();
            for (int i=0; i < Iterations; i++)
            {
                NoInlining();
            }
            sw.Stop();
            Console.WriteLine("No inlining: {0}", sw.ElapsedMilliseconds);
    
            sw = Stopwatch.StartNew();
            for (int i=0; i < Iterations; i++)
            {
                TryCatchThrow();
            }
            sw.Stop();
            Console.WriteLine("try/catch/throw: {0}", sw.ElapsedMilliseconds);
        }
    
        static void SimpleMethod()
        {
            Foo();
        }
    
        [MethodImpl(MethodImplOptions.NoInlining)]
        static void NoInlining()
        {
        }
    
        static void TryCatchThrow()
        {
            try
            {
                Foo();
            }
            catch (Exception)
            {
                throw;
            }
        }
    
        static void Foo() {}
    }
    

    使用/o+ /debug-编译

    结果(三轮):

    简单方法:504、495、489
    无内联:2977、3060、3019
    尝试/捕捉/抛出:5274、4543、5145

    【讨论】:

    • 这也是我的猜测,虽然我没有精力研究。并不是说有什么可以阻止 JIT 注意到在这种情况下 try/catch 是多余的,我怀疑并剥离它......
    • 找不到任何东西来支持我的断言。我相信它要么是抛出,要么是 try/catch 防止内联。我怀疑它实际上可能是 throw vs. catch。
    • 更糟糕的效果? (5274-504)/1000000000 = 当你开始在 foo() 中做某事时没有效果
    • @iik:当你开始在 Foo() 中做任何特别重要的事情时,内联无论如何都会消失。然而,在某些情况下,它可能很重要。如果它不值得做,MS 不会让 JIT 能够内联,不是吗?
    【解决方案2】:

    不,您不需要 try catch。您想要使用第一个函数的唯一原因是,如果您想在进一步处理该函数之前进行一些日志记录或释放资源。

    【讨论】:

      【解决方案3】:

      这两种方法本质上是一样的。在这种情况下,ReSharper 的建议是正确的。

      【讨论】:

        【解决方案4】:

        我想到了一些东西,但我不确定 100%,所以我去检查了。有时我是对的。

        显然,如果您重新抛出异常,这就是您的代码正在执行的操作,您最终可能会更改堆栈跟踪。首先,如果您要编写throw ex;,它将重置堆栈跟踪。其次,即使在写throw; 时,也可能会出现信息丢失的情况。看到这个article 和一些user comments 跟进它。

        当然,这些问题大多与堆栈跟踪和行号有关,这很重要,但我认为它也会影响性能,不仅仅是因为内联(或缺少内联),还因为整个异常捕获并抛出开销,但我没有找到任何具体的内容。

        【讨论】:

          【解决方案5】:

          他们真的不一样。 Throw 本身或 throw ex 会混淆堆栈跟踪信息,并且会使调试变得更加困难。

          捕获异常的最佳理由是向堆栈跟踪添加上下文,例如:

          try {
            Foo();
          }
          catch (Exception e) {
            throw new BetterException("I did something slightly silly", e);
          }
          

          【讨论】:

            【解决方案6】:

            ReSharper 是正确的。除非您真的打算捕获并处理异常,否则包含 try..catch 块是没有意义的。

            【讨论】:

            • @Fred:那应该是try..finally,然后不是try..catch,对吧?
            【解决方案7】:

            是的,try catch 块是多余的。在找到处理异常的 try/catch 之前,堆栈将被展开。

            【讨论】:

              【解决方案8】:

              如果你所做的只是重新抛出你不需要 try/catch 块的异常。通常,您应该仅在可以处理异常时才捕获异常。否则让它们向上传播。

              【讨论】:

                猜你喜欢
                • 2015-02-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-04-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多