14

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

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

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

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

你会如何尝试解决这个问题?

4

19 回答 19

46
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 );
于 2008-09-22T21:24:51.823 回答
22

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

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

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

于 2008-09-22T20:04:27.257 回答
19

我会说什么都不做

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

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

  1. 你知道架构很无聊。
  2. 有很多这种废话。

我说:

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

如果情况如此糟糕,您将立即解决此问题。是的,我知道这听起来很糟糕,而且您可能一开始就用错误修复来拉扯头发,但它可以让您在(大量)代码之前修复有需要/错误的代码,无论它多么糟糕,它实际上都可以正常工作看起来。

一旦您开始赢得战争,您将对代码有更好的处理(由于您的所有重构),您将对获胜的设计有更好的想法。

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

于 2008-09-22T21:24:02.073 回答
12

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

于 2008-09-22T21:54:04.453 回答
9

快速失败

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

于 2008-09-22T20:20:45.113 回答
8

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

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

然而,我认为这些都是可怕的想法,永远不应该这样做,但有人问了这个问题。:)

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

于 2008-09-22T20:08:34.610 回答
7

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

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

于 2008-09-22T20:01:14.007 回答
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();
    }
}
于 2008-09-23T02:03:23.400 回答
3

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

于 2008-09-22T20:03:52.283 回答
2

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

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

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

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

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

于 2008-09-22T20:23:00.660 回答
1

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

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

于 2008-09-22T20:06:39.670 回答
1

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

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

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

于 2008-09-22T20:18:54.710 回答
1

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

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

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

于 2008-09-23T02:16:56.193 回答
1

有趣的问题;非常可怕。

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

解决这个问题,我尝试标记每一行并从一次捕获中跳回来,但没有太多运气。然而,克里斯托弗发现了这样做的正确方法。在 Dot Net Thoughts和Mike Stall 的 .NET 博客上对此有一些有趣的额外讨论

编辑:当然。列出的try-catch/switch-goto解决方案实际上不会编译,因为try标签超出了catch. 任何人都知道进行这样的编译缺少什么?

您可以使用编译器预处理步骤自动执行此操作,或者可以破解Mike Stall 的内联 IL 工具以注入一些错误无知。

( Orion Adrian 关于检查异常并尝试设置下一条指令的回答也很有趣。)

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

于 2008-09-23T16:29:49.607 回答
1

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

于 2011-08-03T15:14:09.030 回答
0

不幸的是,你可能不走运。On Error Resume Next 是一个传统选项,通常不鼓励使用,并且没有与我在 C# 中的知识等效的选项。

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

于 2008-09-22T20:03:00.367 回答
0

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

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

于 2008-09-22T20:11:18.303 回答
-1

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

如果只是需要保持 # 行数,您可以尝试以下操作:

int totalMethodCount = xxx;
for(int counter = 0; counter < totalMethodCount; counter++) {
    尝试 {
        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;
    } 捕捉(异常前){
        // 在这里登录
    }
}

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

于 2008-09-22T20:07:56.047 回答
-7

Hilite 每一行,一次一行,'Surround with' try/catch。这样可以避免您提到的复制粘贴

于 2008-09-22T20:04:12.813 回答