13

In Visual Basic I wrote just On Error Resume Next in the head of my program and errors were suppressed in the entire project.

Here in C# I miss this feature very much. The usual try-catch handling for every single procedure is not only very time-intensive, it brings undesired effects. If an error is encountered, even if handled, the code doesn't continue from the point it occurred. With On Error Resume Next, the code continued from the point of error, skipping just the function call that caused the error.

I am not deeply involved with C# yet, but maybe there exists in C# a better error handling than the primitive try-catch.

I also would like to have the module or function name where the error occured as well as the the line number in my error message. The Exception class doesn't provide that features as far I know. Any ideas (managed, of course, without involving any process classes on my own application)?

How do you handle the errors in bigger projects? I hope I do not have to add a try-catch to each method. Somehow C# throws many errors - that seems to be typical of the language.

My Solution which I found to re-solve several of my problems:

public partial class Form1 : Form
{

    public Form1()
    {
        InitializeComponent();
    }

    [STAThread]
    static void Main()
    {
      Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); //setup global error handler
      Application.Run(new Form1());
    }

    private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {   
            MessageBox.Show("Unhandled exception: " + e.Exception.ToString()); //get all error information with line and procedure call
            Environment.Exit(e.Exception.GetHashCode()); //return the error number to the system and exit the application
    }

    private void button1_Click(object sender, EventArgs e)
    {
        string s = ""; s.Substring(1, 5); //Produce an error
    }

   }
4

6 回答 6

32

Continuing after errors as if nothing's happened is a terrible way of programming.

Can't work out the new balance of the account? That's okay, let's just store it as 0. No-one will ever know, right?

try/catch blocks should actually be relatively rare, because there are relatively few errors you can really recover from. Typically you should have one try/catch block at the top of some logical operation, so that if it fails you can inform the user and continue with other entirely separate operations - or terminate the app completely, depending on the kind of application you're writing. (Web apps are a good example here: you can fail the request, hopefully taking care that you don't have nasty persistent side-effects, and continue to handle other requests.)

Where there are places you legitimately expect errors you can recover from, catch those specific exceptions and handle them appropriately (e.g. falling back to writing to a file if writing to a database fails). Again, these are relatively few and far between. If you find yourself writing a try/catch block in every method (or even every class) then you're probably handling exceptions inappropriately.

I also would like to have the module or function name where the error occured as well the line number in my error message. The Exception class doesn't provide that features as far I experienced.

Yes it does. The stack trace shows the type, method and line number (where available) for each frame in the stack... as well as a (hopefully useful) message, of course. Oh, and potentially a nested exception too, if one failure was caused by another.

Somehow C# throws many errors on execution always, that's language typical.

Nope, that just suggests you're Doing It Wrong.

于 2012-07-21T21:42:49.330 回答
18

No.

Speaking as an ex-VB programmer, please let me assure you: that is the worst and most abused feature ever added to any language ever. Here's the idea instead:

  1. write code that doesn't error ... unless something happens that is actually a problem. This may involve checking your assumptions before you do things; great: do that
  2. only catch problems you were expecting; swallowing all errors is just asking for massive problems

As already noted by Jon, it is actually pretty rare to need exception handling all over the place. Usually you just let an exception bubble up to a higher caller, because something bad just happened. And when I do have a try, it is more commonly try/finally (not try/catch) - with using and lock (etc) being special-cases of those for convenience.

于 2012-07-21T21:43:16.653 回答
4

不,你不能。这在 C# 中是不可能的(也不应该在任何其他语言中)。

在 VB 中这样做的真正用途是在代码的某些部分进行错误处理,就像try / catch. 您启用它、检查、执行您的工作并使用或重定向到遵循不同路径的标签来Err.Number <> 0恢复错误流以处理错误或继续执行。On Error GoTo 0On Error GoTo someErrorCase:

您一定已经学会了独自编程或与不正确的人一起编程。忽略错误是一个坏习惯,不仅如此,仅仅遵循代码是一件可怕的事情。毕竟,错误是可能的。

相信我。我是一名 VB 程序员,当我停下来阅读最佳实践时,这很有启发性。

只是添加一些,也尝试使用Option Explicit,它可能听起来更多的工作声明所有变量,但它会让你对代码更有信心,因为类型检查会限制一些常见的错误。

此外,C# 异常非常有用,并且包含您可能想要的所有信息。如果您没有遇到异常本身的问题,只需打开它并查看其内部异常(我发现我在为 Web 开发时总是在查看内部异常,因为所有代码都处于更高级别)。

于 2012-07-21T21:55:10.603 回答
4

Actually, exceptions are rather the exception, they don't happen all the time. When they do happen, you want to know they happened, and either handle them or shut down your process. Most of the times letting an exception go unhandled is going to lead to very unexpected results.

于 2012-07-21T21:42:18.180 回答
2

你一直在错误地使用那个 VB 特性,你很幸运不能像在 C# 中那样使用它。

在 VB 中使用该功能时,您应该在每次可能导致错误的操作后检查错误状态。如果使用得当,它的代码量不亚于try...catch在每个可能导致错误的操作周围都有块。

因此,如果您认为必须在 C# 中进行更多的错误处理,那么您之前所做的错误处理就太少了。

于 2012-07-21T22:06:08.970 回答
0

“On Error Resume Next”允许“内联错误处理”,这是 VB 中的专家级错误处理。这个概念是逐行处理错误,或者根据错误执行操作,或者在有益时忽略错误 - 但按照编写代码的顺序运行代码而不使用代码跳转。

不幸的是,许多新手使用“On Error Resume Next”通过忽略所有错误来隐藏他们的能力不足或懒惰。Try/catch 是块级错误处理,在 .NET 之前的世界中,它在设计和实现上是中间的。

VB.NET 中“On Error Resume Next”的问题在于,它在执行代码的每一行都加载了 err 对象,因此比 try/catch 慢。

https://msdn.microsoft.com/en-us/library/aa242093(v=vs.60).aspx

据说没有真正 VB 经验的中级 C# 程序员不应该因为他们对另一种“Microsoft Net”语言的奇怪蔑视而试图让 C# 变得愚蠢和功能受限,请考虑以下代码:

//-Pull xml from file and dynamically create a dataset.
 string strXML = File.ReadAllText(@"SomeFilePath.xml");
 StringReader sr = new StringReader(strXML);
 DataSet dsXML = new DataSet();
 dsXML.ReadXml(sr);

string str1 = dsXML.Tables["Table1"].Rows[0]["Field1"].ToString();
string str2 = dsXML.Tables["Table2"].Rows[0]["Field2"].ToStrin();
string str3 = dsXML.Tables["Table3"].Rows[0]["Field3"].ToStrin();
string str4 = dsXML.Tables["Table4"].Rows[0]["Field4"].ToString();
string str5 = dsXML.Tables["Table5"].Rows[0]["Field5"].ToString();

如果 xml 通常具有 Field3 的值但有时没有;我将收到一个恼人的错误,即表格不包含该字段。如果不是,我可以不在乎,因为它不是必需的数据。在这种情况下,ON Error Resume Next 将允许我忽略错误,并且我不必围绕每一行代码编写代码,设置变量检查表、行和列组合是否存在包含方法。这是一个小例子;我可能会从大文件中提取数千个表、列、行组合。此外,这里假设必须以这种方式填充字符串变量。这是未处理的代码,会有麻烦。

考虑一个 VB.NET 和 ON 错误恢复下一个实现:

 On Error Resume Next

        Dim strXML As String = File.ReadAllText("SomeNonExistentFileCausingAnErrorCondition.xml")
        If String.IsNullOrEmpty(strXML) Then
            strXML = strSomeOtherValidXmlThatIUseWhenTheFileIsEmpty
        End If
        Dim srXmL As StringReader = New StringReader(strXML)
        Dim dsXML As DataSet = New DataSet()
        dsXML.ReadXml(srXmL)
        If Err.Number <> 0 Then
            MsgBox(Err.Number & Space(1) & Err.Description)
            Exit Sub
        End If

        Dim str1 As String = dsXML.Tables("Table1").Rows(1)("Field1").ToString()
        Dim str2 As String = dsXML.Tables("Table2").Rows(2)("Field2").ToString()
        Dim str3 As String = dsXML.Tables("Table3").Rows(3)("Field3").ToString()
        Dim str4 As String = dsXML.Tables("Table4").Rows(4)("Field4").ToString()

在上面的代码中,只需要处理一种可能的错误情况;即使加载文件时出错。On Error Resume Next 实际上允许我按预期恢复,这允许我检查字符串条件并使用我的备用字符串(我很清楚我也可以检查文件是否存在并避免文件错误,但如果这是一个没有任何内容的好文件,strXML 将是一个空字符串)。处理了对其余代码至关重要的错误并退出了该方法,因为加载的数据集对于它之外的其余处理至关重要(如果需要,可以通过忽略任何错误来运行处理)。文件错误可以被忽略,因为我忽略了它,或者我可以检查错误情况并记录它。

RAD 开发需要 On Error Resume Next。C# 是我选择的语言,但由于许多原因,它不像 VB 那样是一种 RAD 语言。我希望所有的程序员都意识到几种主要的语言(即C)只是运行并且不会因为未处理的错误而停止执行;在他们认为必要的地方检查他们是开发人员的工作。On Error Resume Next 是微软世界中最接近该范式的东西。

幸运的是,.NET 确实提供了许多高级选择来处理这些情况。我避开了包含。因此,在 C# 中,您必须提高语言知识水平,并且根据 C# 语言规范正确地解决此类问题。考虑一种处理大量重复代码行的解决方案,这些代码行可能包含恼人的丢弃错误:

try
            {
                if (!File.Exists(@"SomeFilePath.xml")) { throw new Exception("XML File Was Not Found!"); }
                string strXML = File.ReadAllText(@"SomeFilePath.xml");
                StringReader sr = new StringReader(strXML);
                DataSet dsXML = new DataSet();
                dsXML.ReadXml(sr);

                Func<string, string, int, string> GetFieldValue = (t, f, x) => (dsXML.Tables[t].Columns.Contains(f) && dsXML.Tables[t].Rows.Count >= x + 1) ? dsXML.Tables[t].Rows[x][f].ToString() : "";

                //-Load data from dynamically created dataset into strings.
                string str1 = GetFieldValue("Table1", "Field1", 0);
                string str2 = GetFieldValue("Table2", "Field2", 0);
                string str3 = GetFieldValue("Table3", "Field3", 0);
                //-And so on.

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            } 

尽管在 try/catch 块中,lambda 函数正在检查从由 xml 动态填充的数据集中提取的每个表、行、列组合是否存在。这可以逐行检查,但需要大量多余的代码(这里我们有相同数量的执行代码,但要维护的书面代码要少得多)。不幸的是,这可能被认为是“单线功能”的另一种不良做法。在 lambdas 和匿名函数的情况下,我打破了这条规则。

由于 .NET 提供了很多方法来检查对象的状态;On Error Resume Next 对 VB 专家来说并不像在 .NET 之前那样重要,但仍然很好用;尤其是当您编写的代码会浪费时间而不是快速和肮脏的代码时。没有一个曾经在专家级别上使用过 VB 的人会声称 On Error Resume Next(内联错误处理)是一种语言中添加的最糟糕的功能。但是,它已被新手广泛滥用。

于 2015-12-12T19:54:20.763 回答