【问题标题】:Try-catch every line of code without individual try-catch blocksTry-catch 每一行代码,无需单独的 try-catch 块
【发布时间】:2008-09-22 19:58:00
【问题描述】:

我目前没有这个问题,但你永远不会知道,而且思想实验总是很有趣。

忽略你的架构甚至要尝试这样做的明显问题,让我们假设你有一些由别人设计的可怕编写的代码,你需要做一个在同一个代码块中进行一系列广泛而多样的操作,例如:

WidgetMaker.SetAlignment(57);
contactForm["Title"] = txtTitle.Text;
Casserole.Season(true, false);
((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true;

乘以一百。其中一些可能有效,另一些可能会出错。您需要的是与“on error resume next”等效的 C#,否则您最终将在多行代码中复制和粘贴 try-catch。

您将如何尝试解决这个问题?

【问题讨论】:

  • on error resume next is of the evil.
  • 我喜欢没有人真正回答这个问题。而是试图给出“正确”的方式。很明显他不想那样。必须对人们有一定的信任,他们知道自己有时在做什么,尤其是当它以这种方式陈述时,天哪。
  • 这很有趣!所以下次有人问,“嘿,我如何编写内存泄漏代码?”每个人都应该解释它而不是反对它?此外,由于他主要问的是“您将如何尝试解决这个问题?”听起来他在征求意见。
  • 你们太棒了。我在问“如果我发现自己悬在悬崖上该怎么办”,而您的回答是“你永远不应该悬在悬崖上”。你为什么不忽略这一点!
  • 没有克里斯托弗·哥伦布,不要航行那么远,你会从世界的边缘掉下来!

标签: c# try-catch expression-trees


【解决方案1】:
public delegate void VoidDelegate();

public static class Utils
{
  public static void Try(VoidDelegate v) {
    try {
      v();
    }
    catch {}
  }
}

Utils.Try( () => WidgetMaker.SetAlignment(57) );
Utils.Try( () => contactForm["Title"] = txtTitle.Text );
Utils.Try( () => Casserole.Season(true, false) );
Utils.Try( () => ((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true );

【讨论】:

  • 这是最有意义的,真的。
  • 从 .net 3.5 开始,您可以使用 Action 而不是 voidDelegate。
  • 我喜欢这个!这就像在 C# 中从 PHP 中获取 @ 运算符。 php.net/manual/en/language.operators.errorcontrol.php 这个问题我不用写。
  • 即使您开始传递参数,Action 为何也能在这里工作?编译器是否会为您处理这个问题...比如忽略或删除参数?
  • 这里的主要问题是“catch {}”,它会捕获不应该被捕获的东西,包括 CLR 状态中的低级错误。
【解决方案2】:

重构为单独的、命名良好的方法:

AdjustFormWidgets();
SetContactTitle(txtTitle.Text);
SeasonCasserole();

每一个都受到适当的保护。

【讨论】:

    【解决方案3】:

    我会说什么都不做

    是的,没错,什么都不做。

    你已经清楚地向我指出了两件事:

    1. 你知道架构很烂。
    2. 这种废话太多了。

    我说:

    • 什么都不做。
    • 添加一个全局错误处理程序,以便在每次出现故障时向您发送一封电子邮件。
    • 等到有东西翻倒(或测试失败)
    • 更正(根据需要重构在页面范围内)。
    • 每次出现问题时重复。

    如果情况如此糟糕,您很快就会解决这个问题。是的,我知道这听起来很糟糕,而且您可能一开始就通过错误修复来拉扯头发,但它可以让您(大量)代码之前修复有需要/错误的代码不管它看起来多么糟糕,它实际上可能会起作用

    一旦你开始赢得战争,你将对代码有更好的处理(由于你所有的重构)你将有一个更好的想法来为它赢得设计..

    尝试将所有问题都包装在气泡包装中可能需要很长时间才能完成,而且您仍然不会更接近解决问题。

    【讨论】:

    • 我在无数项目上的确切方法 - 很好的解释
    【解决方案4】:

    很明显,您将在 VB.NET 中编写代码,该代码实际上确实有 On Error Resume Next,然后将其以 DLL 的形式导出到 C#。其他任何事情都只是贪吃 惩罚。

    【讨论】:

    • 我相信这是一个有效的解决方案,可以解决问题中提出的难题,所以我接受答案!
    【解决方案5】:

    Fail Fast

    为了详细说明,我想我是在质疑这个问题。如果抛出异常,为什么您希望您的代码像什么都没发生一样继续执行?要么您期望在某些情况下出现异常,在这种情况下,您围绕该代码编写一个 try-catch 块并处理它们,或者出现意外错误,在这种情况下,您应该希望您的应用程序中止、重试或失败。不要像受伤的僵尸一样呻吟着“大脑”。

    【讨论】:

    • Wiki 链接“答案”真的没那么有用.. 为什么不尝试扩展呢?
    【解决方案6】:

    这是拥有预处理器有用的事情之一。您可以定义一个吞噬异常的宏,然后使用快速脚本将该宏添加到所有行。

    所以,如果这是 C++,你可以这样做:

    #define ATTEMPT(x) try { x; } catch (...) { }
    // ...
    ATTEMPT(WidgetMaker.SetAlignment(57));
    ATTEMPT(contactForm["Title"] = txtTitle.Text);
    ATTEMPT(Casserole.Season(true, false));
    ATTEMPT(((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true);
    

    不幸的是,似乎没有多少语言像 C/C++ 那样包含预处理器。

    您可以创建自己的预处理器并将其添加为预构建步骤。如果您想完全自动化它,您可能会编写一个预处理器,它将获取实际的代码文件并自行添加 try/catch 内容(因此您不必手动将那些 ATTEMPT() 块添加到代码中) .确保它只修改了它应该修改的行可能很困难(必须跳过变量声明、循环构造等,以免破坏构建)。

    但是,我认为这些想法很糟糕,永远不应该这样做,但有人提出了这个问题。 :)

    真的,你永远不应该这样做。您需要找到导致错误的原因并修复它。吞下/忽略错误是一件坏事,所以我认为这里的 正确 答案是“修复错误,不要忽略它!”。 :)

    【讨论】:

    • 嘿,真正回答问题的人,你得到了我的投票;)
    • 问题是这仍然无法按预期工作,至少在没有一些额外思考的情况下不会。如果你这样做,那么几乎可以保证你会将变量移出范围并破坏你的构建。
    • 这就是为什么我说你需要小心跳过变量声明以及其他事情。
    • 即使你不破坏你的构建,这会很困难,除了变量声明和循环结构之外,还有很多东西需要实际寻找。我知道你是在暗示他真的要坚持下去(恰恰相反)。
    • 但这正是尝试编写一个工具来获得这种乐趣的原因! (或令人难以置信的痛苦)。 :)
    【解决方案7】:

    On Error Resume Next 在 C# 世界中是一个非常糟糕的主意。将等效项添加到 On Error Resume Next 也不会真正帮助您。它只会让您处于糟糕的状态,这可能会导致更细微的错误、数据丢失以及可能的数据损坏。

    但是为了让提问者得到应有的回报,您可以添加一个全局处理程序并检查 TargetSite 以查看哪个方法无效。然后你至少可以知道它在哪条线上。下一部分将尝试找出如何以与调试器相同的方式设置“下一条语句”。希望此时您的堆栈不会解开,或者您可以重新创建它,但这当然值得一试。但是,如果采用这种方法,代码每次都必须在调试模式下运行,这样才能包含调试符号。

    【讨论】:

      【解决方案8】:

      正如有人提到的,VB 允许这样做。在 C# 中以同样的方式做怎么样?输入可信赖的反射器:

      这个:

      Sub Main()
          On Error Resume Next
      
          Dim i As Integer = 0
      
          Dim y As Integer = CInt(5 / i)
      
      
      End Sub
      

      翻译成这样:

      public static void Main()
      {
          // This item is obfuscated and can not be translated.
          int VB$ResumeTarget;
          try
          {
              int VB$CurrentStatement;
          Label_0001:
              ProjectData.ClearProjectError();
              int VB$ActiveHandler = -2;
          Label_0009:
              VB$CurrentStatement = 2;
              int i = 0;
          Label_000E:
              VB$CurrentStatement = 3;
              int y = (int) Math.Round((double) (5.0 / ((double) i)));
              goto Label_008F;
          Label_0029:
              VB$ResumeTarget = 0;
              switch ((VB$ResumeTarget + 1))
              {
                  case 1:
                      goto Label_0001;
      
                  case 2:
                      goto Label_0009;
      
                  case 3:
                      goto Label_000E;
      
                  case 4:
                      goto Label_008F;
      
                  default:
                      goto Label_0084;
              }
          Label_0049:
              VB$ResumeTarget = VB$CurrentStatement;
              switch (((VB$ActiveHandler > -2) ? VB$ActiveHandler : 1))
              {
                  case 0:
                      goto Label_0084;
      
                  case 1:
                      goto Label_0029;
              }
          }
          catch (object obj1) when (?)
          {
              ProjectData.SetProjectError((Exception) obj1);
              goto Label_0049;
          }
      Label_0084:
          throw ProjectData.CreateProjectError(-2146828237);
      Label_008F:
          if (VB$ResumeTarget != 0)
          {
              ProjectData.ClearProjectError();
          }
      }
      

      【讨论】:

      • 非常有趣 :):) 鉴于这个问题的本质,我会建议原始发帖人在他遇到问题时立即实施这种方法 - 这是最接近他的问题。它与接受的答案相同(在 VB 中执行并在接下来的错误恢复中使用)并且它在 C# 中。我还建议他面对有人在他之后不得不维持这种状态的愤怒。这与心态有关……您要么想尝试捕获一千行长方法中的每一行,要么重构该方法并使整个事情变得更好。不需要假设的想法实验。
      • @Marek,重构是一个伟大的目标,但有时您必须在大修时保持整个混乱状态。当用户因为未处理的异常关闭应用程序而丢失数据时会讨厌它。他们并没有指出是哪个傻瓜写的弄得一团糟,而是现在负责的开发人员。
      【解决方案9】:

      重写代码。尝试找到在逻辑上相互依赖的语句集,这样如果一个失败,那么下一个语句就没有意义了,如果你想忽略然后继续。

      【讨论】:

        【解决方案10】:

        这可以帮助您确定问题最多的部分。

        @JB王 谢谢你提醒我。 Logging 应用程序块有一个可用于跟踪事件的 Instrumentation Event,您可以在 MS Enterprise 库文档中找到更多信息。

        Using (New InstEvent)
        <series of statements> 
        End Using
        

        此使用中的所有步骤都将被跟踪到一个日志文件,您可以将其解析出来以查看日志中断的位置(例如抛出)并识别高违规者。

        重构确实是你最好的选择,但如果你有很多,这可能会帮助你找出最严重的违规者。

        【讨论】:

          【解决方案11】:

          可以使用 goto,但还是很乱。

          我实际上想要一种单语句 try-catch 有一段时间了。这在某些情况下会很有帮助,例如添加日志代码或您不想在主程序流失败时中断的东西。

          我怀疑一些与 linq 相关的功能可以做一些事情,但目前还没有时间研究它。如果您能找到一种将语句包装为匿名函数的方法,然后使用另一个方法在 try-catch 块中调用它,它会起作用……但目前还不确定这是否可能。

          【讨论】:

            【解决方案12】:

            如果您可以让编译器为您提供此代码的表达式树,那么您可以通过将每个语句替换为包含原始语句的新 try-catch 块来修改该表达式树。这并不像听起来那么牵强。对于 LINQ,C# 获得了将 lambda 表达式捕获为表达式树的能力,可以在运行时在用户代码中进行操作。

            这种方法在今天的 .NET 3.5 中是不可能的——如果没有其他原因,只是 System.Linq.Expressions 中缺少“try”语句。但是,一旦 DLR 和 LINQ 表达式树的合并完成,它在 C# 的未来版本中很可能是可行的。

            【讨论】:

              【解决方案13】:

              为什么不在 c# 中使用反射?您可以创建一个反映代码的类,并使用 #s 行作为在每个单独的 try/catch 块中放置内容的提示。这有几个优点:

              1. 它稍微不那么难看,因为它实际上并不需要修改源代码,而且您只能在调试模式下使用它。
              2. 在实现 c# 时,您会学到一些有趣的东西。

              但是,我建议您不要这样做,除非您当然要接管某些工作的维护,并且您需要处理异常以便修复它们。不过写起来可能会很有趣。

              【讨论】:

              • 乐趣总是好的。很高兴看到更多人参与其中。
              【解决方案14】:

              有趣的问题;太可怕了。

              如果你能使用宏就好了。但这是 C#,所以你可以用一些预处理器工作或一些外部工具来解决它,将你的行包装在单独的 try-catch 块中。不确定您的意思是不想手动包装它们,还是想避免尝试完全

              搞砸了这个,我尝试标记每一行并从一个捕获中跳回来,但没有太多运气。但是,Christopher uncovered the correct way to do this。有一些interesting additional discussion of this at Dot Net ThoughtsMike Stall's .NET Blog

              编辑:当然。列出的try-catch / switch-goto 解决方案实际上不会编译,因为try 标签超出catch 的范围。有人知道编译这样的东西缺少什么吗?

              您可以使用编译器预处理步骤自动执行此操作,也可以破解 Mike Stall's Inline IL tool 以注入一些错误忽略。

              Orion Adrian's answer about examining the Exception 并尝试设置下一条指令也很有趣。)

              总而言之,这似乎是一个有趣且有启发性的练习。当然,您必须决定在什么时候模拟 ON ERROR RESUME NEXT 的努力超过修复代码的努力。 :-)

              【讨论】:

                【解决方案15】:

                在应用程序的UnhandledException Event 中捕获错误。这样,甚至可以将未处理的异常记录到发件人以及开发人员认为合理的任何其他信息。

                【讨论】:

                  【解决方案16】:

                  很遗憾,您可能运气不佳。 On Error Resume Next 是一个传统选项,通常不鼓励使用,并且没有我在 C# 方面的知识。

                  我建议将代码保留在 VB 中(这听起来像是源代码,鉴于您对 OnError ResumeNext 的特定请求)并与 C# dll 或 exe 进行交互,以实现您需要的任何新代码。然后进行预制重构以使代码安全,并在执行此操作时将此安全代码转换为 C#。

                  【讨论】:

                    【解决方案17】:

                    您可以查看集成企业库的异常处理组件,了解如何处理未处理的异常。

                    如果这是针对 ASP.Net 应用程序,那么 Global.asax 中有一个名为“Application_Error”的函数,在大多数情况下都会调用该函数,而另一种情况通常是灾难性故障。

                    【讨论】:

                      【解决方案18】:

                      忽略所有你想避免这样做的原因......

                      如果只是需要减少行数,您可以尝试以下方法:

                      int totalMethodCount = xxx; for(int counter = 0; counter

                      但是,如果您尝试重用任何调用结果,则必须注意变量范围。

                      【讨论】:

                        【解决方案19】:

                        Hilite 每一行,一次一个,'Surround with' try/catch。这样就避免了你提到的复制粘贴

                        【讨论】:

                        • 这实际上不起作用。你会破坏范围。诠释我;我 = 5;不能被尝试抓住。
                        • 你到底在说什么?海报问题中到底哪里有变量赋值?
                        • 这意味着它是“100 倍”,这意味着那里有很多代码没有呈现出来。在这种情况下,肯定会有一个或两个变量赋值以及十几个其他不能单独尝试/捕获的构造。
                        • 100 X 那四行代码仍然呈现零分配。
                        • @mattlant 我会给你+1这个帖子中最有趣的答案,但我担心你是认真的:):):)
                        猜你喜欢
                        • 2017-11-29
                        • 2019-12-04
                        • 2018-03-04
                        • 1970-01-01
                        • 2017-08-09
                        • 2016-06-06
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多