尝试捕获每行代码而不使用单独的try-catch块

时间:2008-09-22 19:58:01

标签: c# try-catch expression-trees

我目前没有这个问题,但你永远不知道,并认为实验总是很有趣。

忽略你的架构甚至要尝试这个时必须遇到的明显问题,让我们假设你有一些别人设计的可怕编写的代码,你需要做一个在同一代码块中进行各种各样的操作,例如:

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

乘以一百。其中一些可能有效,另一些可能会出错。你需要的是C#相当于“on next resume next”,否则你将最终复制并粘贴许多代码行的try-catches。

你会如何解决这个问题?

19 个答案:

答案 0 :(得分:45)

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

答案 1 :(得分:22)

重构为具有明确名称的个别方法:

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

每一项都得到适当的保护。

答案 2 :(得分:19)

我会说什么都不做

是的,没错,没有。

你已经清楚地向我发现了两件事:

  1. 你知道这个架构很糟糕。
  2. 有很多废话。
  3. 我说:

    • 什么都不做。
    • 添加一个全局错误处理程序,以便每次发送时都向您发送电子邮件。
    • 等到某些事情发生(或未通过测试)
    • 纠正(必要时重构在页面范围内)。
    • 每次出现问题时重复。

    如果情况那么糟糕,你会立即清理它。是的,我知道这听起来很糟糕,你可能会开始用错误修正来解决你的问题,但它会让你修复有需要/错误的代码(大量)代码无论它看起来多么糟糕,它实际上都可以正常工作

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

    尝试将所有这些包装在气泡包装中可能需要很长时间才能完成,您仍然无法更接近解决问题。

答案 3 :(得分:12)

很明显,您在VB.NET中编写代码,实际上有On Error Resume Next,并将其导出到DLL中的C#。其他任何东西都只是一个暴食者  惩罚。

答案 4 :(得分:9)

Fail Fast

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

答案 5 :(得分:7)

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

所以,如果这是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()块添加到代码中) 。确保它只修改了它应该是困难的行(必须跳过变量声明,循环结构等,你不会破坏构建)。

但是,我认为这些都是可怕的想法,永远不应该这样做,但问的是。 :)

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

答案 6 :(得分:7)

On Error Resume Next在C#世界中是一个非常糟糕的主意。也不会添加相当于On Error Resume Next的实际帮助你。它所做的只是让你处于一个糟糕的状态,这可能会导致更微妙的错误,数据丢失和可能的数据损坏。

但是为了让提问者得到应有的答案,你可以添加一个全局处理程序并检查TargetSite以查看borked的方法。然后你至少可以知道它是什么线。接下来的部分是试图找出如何设置“下一个语句”的方式与调试器的工作方式相同。希望你的筹码在这一点上不会被解开,或者你可以重新创造它,但它肯定值得一试。但是,根据这种方法,代码必须每次都以调试模式运行,以便包含调试符号。

答案 7 :(得分:4)

正如有人提到的,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();
    }
}

答案 8 :(得分:3)

重写代码。尝试找到逻辑上相互依赖的语句集,这样如果一个语句失败了,那么下一个语句就没有意义了,如果你想忽略它们的结果,那么将它们放到自己的函数中并将它们放在它们周围。那并继续。

答案 9 :(得分:2)

这可以帮助您识别问题最多的部分。

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

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

此使用中的所有步骤都将追溯到一个日志文件,您可以解析它以查看日志中断的位置(ex被抛出)并识别高级违规者。

重构真的是你最好的选择,但如果你有很多,这可以帮助你找出最严重的罪犯。

答案 10 :(得分:1)

可以使用goto,但它仍然很混乱。

我实际上想要一段时间的单一语句try-catch。在某些情况下它会很有用,例如添加日志代码或者如果失败则不想中断主程序流。

我怀疑与linq相关的一些功能可以做些什么,但目前还没有时间研究它。如果你可以找到一种方法将一个语句包装成一个匿名函数,那么使用另一个方法在try-catch块中调用它可以工作......但不确定这是否可能。

答案 11 :(得分:1)

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

答案 12 :(得分:1)

有趣的问题;太可怕了。

如果可以使用宏,那就太好了。但这是对C#的抨击,所以你可以通过一些预处理器工作或一些外部工具来解决它,将你的行包装在单独的try-catch块中。不确定您是否意味着您不想手动包装它们或者您想要完全避免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的努力超过了修复代码的努力。 : - )

答案 13 :(得分:1)

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

  1. 它稍微不那么难看,因为它实际上并不需要破坏你的源代码,你只能在调试模式下使用它。
  2. 在实施时,您了解了有关c#的一些有趣内容。
  3. 但是我建议不要这样做,除非你正在接管某些工作的维护,你需要处理异常,以便你可以修复它们。写作可能会很有趣。

答案 14 :(得分:1)

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

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

答案 15 :(得分:0)

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

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

答案 16 :(得分:0)

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

如果只是需要保持行数减少,你可以尝试类似:

int totalMethodCount = xxx;
for(int counter = 0; counter < totalMethodCount; counter++) {
    try {
        if (counter == 0) WidgetMaker.SetAlignment(57);
        if (counter == 1) contactForm["Title"] = txtTitle.Text;
        if (counter == 2) Casserole.Season(true, false);
        if (counter == 3) ((RecordKeeper)Session["CasseroleTracker"]).Seasoned = true;
    } catch (Exception ex) {
        // log here
    }
}

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

答案 17 :(得分:0)

不幸的是,你可能运气不好。 On Error Resume Next是一个遗留选项,通常非常不鼓励,并且与C#中的知识不相同。

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

答案 18 :(得分:-7)

Hilite每一行,一次一个,'环绕'尝试/捕获。这避免了你提到的复制粘贴