17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)

17.常见的异常类有哪些?

Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, to malfunctioning web service calls, to errors writings files to disk, exceptions provide our applications with a graceful way of recovering from unexpected or unplanned errors without passing the frustrations of a non-functional application to our end users. In this article, I would like to discuss 5 common .NET exceptions. While I have no doubt these exceptions are common even outside of the realm of EE, I am basing my list on questions I have witnessed on the site. This article is targeted at the novice to intermediate programmers.

我们在EE的许多人都在编写代码。 我们中的许多人都编写了出色的代码。 就像我们许多人编写容易发生异常的代码一样。 众所周知,异常是一种处理错误的机制,这些错误通常是我们无法控制的。 从数据库错误到出现故障的Web服务调用,再到将文件写入磁盘的错误,异常为我们的应用程序提供了一种从意外错误或计划外错误中恢复的优美方法,而不会将无法正常运行的应用程序的烦恼传递给我们的最终用户。 在本文中,我想讨论5个常见的.NET异常。 尽管毫无疑问,即使在EE领域之外,这些例外也很常见,但我还是根据在现场看到的问题来列出我的清单。 本文针对中级程序员的新手。

It should be noted that any of the following exceptions can be used however a designer decides to use them. What I mean by this is that if I wanted to, I could use a StackOverflowException to indicate when a variable was null. I agree: that doesn't make much sense, but just know that I could. The following sections assume the intuitive meaning of their respective exceptions.

应当注意,可以使用以下任何例外,但是设计者决定使用它们。 我的意思是,如果愿意,我可以使用StackOverflowException来指示变量何时为空。 我同意:这没有多大意义,但只知道我可以。 以下各节假定它们各自例外的直观含义。

For ease of navigation, this article is laid out with the following exceptions, listed in order of appearance within the article

为了便于浏览,本文的布局与以下例外情况相同,在文章中以出现顺序列出

1. NullReferenceException 1. NullReferenceException

2. FormatException 2. FormatException

3. StackOverflowException 3. StackOverflowException

4. InvalidOperationException 4. InvalidOperationException

5. "First Chance" Exception 5. “第一机会”异常

1. NullReferenceException (1. NullReferenceException)

If exceptions are the red-headed step-children of the .NET family, then NullReferenceException is the cousin who spent most of his adolescence in juvenile detention (and is probably now in prison). NullReferenceException is probably the single most common exception found in the wild. It's an easy exception to understand: when dealing with reference types, some variable hasn't been assigned a valid reference to an instantiated object. In the simplest case, you haven't said "new [something]" at some point. For example:

如果例外是.NET家族的红发继子,则NullReferenceException是堂兄,他的青春期大部分时间都在少年拘留中(现在可能正在监狱中)。 NullReferenceException可能是在野外发现的最常见的单个异常。 这是一个容易理解的例外:在处理引用类型时,尚未向某个变量分配对实例化对象的有效引用。 在最简单的情况下,您有时没有说“ new [something]”。 例如:

C#

C#

public partial class Form1 : System.Windows.Form
{
    private System.Windows.TextBox mySpecialTB;

    private void button1_Click(object sender, System.EventArgs e)
    {
        this.mySpecialTB.Text = "Hello World!";
    }
}

VB

VB

Public Class Form1
    Private mySpecialTB As TextBox

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.mySpecialTB.Text = "Hello World!"
    End Sub
End Class

In the code samples above, you will note that there is an attempt to assign to the Text property of "mySpecialTB"; however, there was never an assignment to this variable. You won't find a "mySpecialTB = new TextBox()" line above. Because this particular assignment never occurred, the private TextBox is always null, and we get this exception when we try to assign to the Text property.

在上面的代码示例中,您将注意到尝试分配给Text属性“ mySpecialTB”; 但是,此变量从未赋值。 您不会在上面找到“ mySpecialTB = new TextBox()”行。 因为从未发生此特定分配,所以私有TextBox始终为null,并且当我们尝试分配给Text属性时会遇到此异常。

It does get a bit deeper than that when dealing with objects that contain other objects, and I believe that's where some of the confusion lies. Let's create a new class:

它确实比处理包含其他对象的对象时要深一些,我相信这就是其中一些困惑所在。 让我们创建一个新类:

C#

C#

public partial class Form1 : System.Windows.Form
{
    private MySpecialClass msc;

    private void button1_Click(object sender, System.EventArgs e)
    {
        this.msc = new MySpecialClass();

        System.Windows.MessageBox.Show(msc.MySpecialTextBox.Text);
    }
}

public class MySpecialClass
{
    private System.Windows.TextBox mySpecialTB;

    public System.Windows.TextBox MySpecialTextBox { get { return this.mySpecialTB; } }
}

VB

VB

Public Class Form1
    Private msc As MySpecialClass

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.msc = New MySpecialClass()

        MessageBox.Show(Me.msc.MySpecialTextBox.Text)
    End Sub
End Class

Public Class MySpecialClass
    Private mySpecialTB As TextBox

    Public ReadOnly Property MySpecialTextBox() As TextBox
        Get
            Return Me.mySpecialTB
        End Get
    End Property
End Class

Can you spot the NullReferenceException? We instantiated "msc," so where does the problem lie? The keen reader will note that even though we instantiated our private variable in the Form1 code by using the default constructor for MySpecialClass, there is nothing inside the MySpecialClass definition that will instantiate the "mySpecialTB" field of MySpecialClass. This is easy enough to see in the debugger. Mousing over the appropriate member(s), we can see which one is the offending member.

您可以发现NullReferenceException吗? 我们实例化了“ msc”,那么问题出在哪里呢? 敏锐的读者会注意到,即使我们通过使用MySpecialClass的默认构造函数在Form1代码中实例化了私有变量,MySpecialClass定义内也没有任何东西可以实例化MySpecialClass的“ mySpecialTB”字段。 这很容易在调试器中看到。 将鼠标悬停在适当的成员上,我们可以看到哪个成员是有罪的成员。

17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第1张图片
17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第2张图片

Your primary focus when attempting to correct a NullReferenceException is to track down which member(s) is null and to make sure all members have been properly instantiated. In the above example, all that would be required would be to either code a constructor which instantiates "mySpecialTB" automatically, or to instantiate the MySpecialTextBox property from within the Form1 code. The former is usually preferable.

尝试更正NullReferenceException时,您的主要重点是跟踪哪些成员为null,并确保所有成员均已正确实例化。 在上面的示例中,所需要做的只是编码一个自动实例化“ mySpecialTB”的构造函数,或者从Form1代码中实例化MySpecialTextBox属性。 前者通常是可取的。

2. FormatException (2. FormatException)

If you've ever worked with the string.Format() method, then running into this exception might throw you for a loop. Even though this exception sounds like you have mistakenly placed a curly brace in your format string for string.Format(), it is related to another group of methods. .NET has a Convert class which has several static (Shared) methods used to convert values between types. One popular use of these methods that I see discussed at EE is for converting string data to numeric data. This is fine, provided the data you are converting actually represents a number. FormatException is raised whenever you call one of the Convert class' methods and the data you pass it doesn't represent a number. Take for example the following code:

如果您曾经使用过string.Format()方法,则遇到此异常可能会导致循环。 即使此异常听起来像您错误地在string.Format()的格式字符串中放置了花括号,它也与另一组方法有关。 .NET具有一个Convert类,该类具有几个用于在类型之间转换值的静态(共享)方法。 我在EE上讨论了这些方法的一种流行用法,就是将字符串数据转换为数字数据。 如果Convert类的方法之一,并且传递给它的数据不代表数字时,都会引发FormatException 。 以下面的代码为例:

C#

C#

private void button1_Click(object sender, System.EventArgs e)
{
    string test = "Hello World!";
    int converted = Convert.ToInt32(test);
}

VB

VB

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim test As String = "Hello World!"
    Dim converted As Integer = Convert.ToInt32(test)
End Sub

Hopefully, you realize that "Hello World!" doesn't constitute a valid integer value. As such, when you try to pass this string to Convert.ToInt32(), a FormatException is raised.

希望您意识到“ Hello World!” 不构成有效的整数值。 这样,当您尝试将此字符串传递给Convert.ToInt32()时 ,将引发FormatException

17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第3张图片
17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第4张图片

What needs to happen to resolve this error is intuitive: ensure that you are passing only "valid" numeric values to any of Convert's methods. How do you do that, though? If you know, beyond a shadow of a doubt, that there is no chance any variables you pass to Convert will hold anything other than valid numeric values, then using the Convert class should cause you no grief. Personally, though, I have gotten in the habit of using the TryParse family of calls to convert data. For instance, the int (Integer) struct has a TryParse method:

解决此错误所需的操作很直观:确保仅将“有效”数字值传递给Convert的任何方法。 不过,您该怎么做? 如果您毫无疑问地知道,传递给Convert的任何变量都不会保存有效数值以外的任何其他东西,那么使用Convert类应该不会给您带来麻烦。 不过,就我个人而言,我已经习惯于使用TryParse系列调用来转换数据。 例如, int(Integer)结构具有一个TryParse方法:

C#

C#

string test = "Hello World!";
int converted;

if (int.TryParse(test, out converted))
{
    // Conversion succeeded!
}

VB

VB

Dim test As String = "Hello World!"
Dim converted As Integer

If Integer.TryParse(test, converted) Then
    ' Conversion succeeded!
End If

Notice above that I'm using the TryParse call as a test in an "if" condition. This is because TryParse returns true if the value stored in the first parameter represents a valid integer, or returns false if it does not. If the value can be converted, then the second parameter is passed by reference and will hold the converted value when the method returns. There will be no exceptions raised using this method, even if the first parameter happens to be null!

请注意,上面我在“ if”条件下将TryParse调用用作测试。 这是因为,如果存储在第一个参数中的值表示有效整数,则TryParse返回true,否则返回false。 如果该值可以转换,则第二个参数由引用传递,并在方法返回时保留转换后的值。 即使第一个参数恰好为空,使用此方法也不会引发任何异常!

3. StackOverflowException (3. StackOverflowException)

Although it sounds like it, this exception is not a devious ploy by those participating at another well-know Q&A web site. A StackOverflowException is a bit like an OutOfMemoryException in that your program has exceeded some memory boundary in the system. What is that boundary? Well, it's the stack! The stack is reserved for static memory allocations--things like value-type declarations and method calls. The stack, much like system memory (which is where the stack is located), is finite. If you have too many static memory allocations, you will run into a StackOverflowException.

尽管听起来很像,但参加另一个知名Q&A网站的人员并没有采取这种欺骗手段。 StackOverflowException有点像OutOfMemoryException,因为您的程序已超出系统中的某些内存边界。 那是什么边界? 好吧,就是堆栈! 堆栈是为静态内存分配保留的,诸如值类型声明和方法调用之类的东西。 堆栈很像系统内存(堆栈所在的位置),它是有限的。 如果静态内存分配过多,则会遇到StackOverflowException

The most likely culprit in a StackOverflowException is an incorrectly terminated recursive method or property. Yes, I said "property," and I'll discuss that momentarily. First, let's examine the recursive method.

StackOverflowException中最可能的罪魁祸首是错误终止的递归方法或属性。 是的,我说“财产”,我会暂时讨论。 首先,让我们研究一下递归方法。

递归方法上的StackOverFlowException (StackOverFlowException on a Recursive Method)

C#

C#

class Program
{
    private static int hitCount;

    static void Main(string[] args)
    {
        hitCount = 0;
        MyRecursiveExample(null);
    }

    static void MyRecursiveExample(string value)
    {
        hitCount++;

        if (value == null)
        {
            MyRecursiveExample(value);
        }
        else
        {
            System.Console.WriteLine(value);
        }
    }
}

VB

VB

Module Module1
    Private hitCount As Long

    Sub Main()
        hitCount = 0
        MyRecursiveExample(Nothing)
    End Sub

    Sub MyRecursiveExample(ByVal value As String)
        hitCount += 1

        If value Is Nothing Then
            MyRecursiveExample(value)
        Else
            Console.WriteLine(value)
        End If
    End Sub
End Module
not

be null. As such, the function will recurse indefinately. Well, not indefinately: until you get a StackOverflowException! I've included "hitCount" to show you how "long" it takes to get to this point. (The count you see in the images is not fixed--it will be different based on your logic.)

null 。 因此,该函数将不确定地递归。 好吧,不是不确定地:在您得到StackOverflowException之前 ! 我添加了“ hitCount”来向您展示达到这一点需要多长时间。 (您在图像中看到的计数不是固定的,具体取决于您的逻辑。)

In the following images, you can see the results of our flawed logic above.

在以下图像中,您可以看到上面我们有缺陷的逻辑的结果。

17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第5张图片
17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第6张图片

递归属性上的StackOverFlowException (StackOverFlowException on a Recursive Property)

之前,我说过,属性可能存在缺陷。 这是该声明的演示:

C#

C#

class MyExampleClass
{
    private string _somePrivateMember;

    public string MyProperty
    {
        get { return this.MyProperty; }
        set { this.MyProperty = value; }
    }
}

VB

VB

Class MyExampleClass
    Private _somePrivateMember As String

    Public Property MyProperty() As String
        Get
            Return Me.MyProperty
        End Get
        Set(ByVal value As String)
            Me.MyProperty = value
        End Set
    End Property
End Class

get and set operations. This results in an endless loop, and susequently an overlfow of the stack:

获取设置操作。 这将导致无限循环,并因此导致堆栈超载:

17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第7张图片
17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第8张图片

VB will give you a hint with a compiler warning that you are about to engage in an infinite recursion scenario, but it won't stop you from executing the code (as seen in the screenshot above). C# will not indicate to you at all. (While C# did note that the private field "_somePrivateMember" was never used, you cannot count on this to always be the case as you may have referred to this field elsewhere within the class.) Be sure you are referring to the backing field ("_somePrivateMember" in the example above) for the property within your gets and sets.

VB会提示您编译器警告您将要进行无限递归方案,但是它不会阻止您执行代码(如上面的屏幕快照所示)。 C#完全不会向您显示。 (虽然C#确实注意到私有字段“ _somePrivateMember”从未使用过,但您不能指望总是这样,因为您可能已经在类中的其他地方引用了该字段。)请确保您引用的是后备字段( getset中的属性的“ _somePrivateMember”(在上面的示例中)。

Although I focused on recursion in this example, please don't think this is the only scenario that can cause a StackOverflowException, although it is the most likely. Although improbable, you could be potentially so deep in a method call stack that you overflow the stack.

尽管在此示例中我专注于递归,但请不要认为这是唯一可能导致StackOverflowException的情况 ,尽管它很可能会发生。 尽管不太可能,但是您可能会陷入方法调用堆栈的深处,以至于堆栈溢出。

4. InvalidOperationException (4. InvalidOperationException)

InvalidOperationException can occur for any number of error conditions. However, for the purposes of this article the condition I would like to focus on is that related to GUI programming. I see this group of questions all the time: "Why doesn't my GUI show my logic's progress," "Why does my GUI lock up when I run function X()," "How do I display a progress bar for my worker function?" The answer is always the same: "Use some form of threading." Initially the problem seems solved. However, this is usually where InvalidOperationException makes its appearance. An InvalidOperationException is raised whenever you try interface with a GUI component directly from a thread other than the one which created the component. Here is an example:

对于任何数量的错误情况,都可能发生InvalidOperationException 。 但是,出于本文的目的,我要关注的条件是与GUI编程有关的条件。 我一直都在看这组问题:“为什么我的GUI不能显示我的逻辑进度,”“为什么当我运行函数X()时GUI会被锁定,”“如何为工人显示进度条功能?” 答案总是相同的:“使用某种形式的线程”。 最初,问题似乎已解决。 但是,通常是在InvalidOperationException出现的地方。 每当您尝试直接从创建组件的线程之外的线程与GUI组件进行接口时,都会引发InvalidOperationException 。 这是一个例子:

C#

C#

public partial class Form1 : System.Windows.Forms.Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void startButton_Click(object sender, System.EventArgs e)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(RunCustomLogic), "Done");
    }

    private void RunCustomLogic(object o)
    {
        for (int i = 0; i < 100; i++)
        {
            System.Threading.Thread.Sleep(10);
        }

        this.resultsBox.Text = o.ToString();
    }
}

VB

VB

Public Class Form1

    Private Sub startButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles startButton.Click
        System.Threading.ThreadPool.QueueUserWorkItem(New System.Threading.WaitCallback(AddressOf RunCustomLogic), "Done")
    End Sub

    Sub RunCustomLogic(ByVal o As Object)
        For i As Integer = 0 To 99
            System.Threading.Thread.Sleep(10)
        Next

        Me.resultsBox.Text = o.ToString()
    End Sub
End Class

InvalidOperationException to rear its ugly head, as seen in the following images:

InvalidOperationException抬起其丑陋的头,如下图所示:

17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第9张图片
17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们)_第10张图片

In .NET, controls may only be updated from the thread on which they were created. For most everybody, this will be the main thread (i.e. the one that calls Main() initially). The way to accommodate for this is by "invoking" the update. In my previous example, I would create a new method that did the updating, but inside of this method I would check whether or not I need to "invoke" the update. This is accomplished via checking the InvokeRequired property. Here is an example of the updated logic:

在.NET中,控件只能从创建它们的线程中进行更新。 对于大多数人来说,这将是主线程(即最初调用Main()的线程)。 适应此问题的方法是“调用”更新。 在我之前的示例中,我将创建一个执行更新的新方法,但是在该方法内部,我将检查是否需要“调用”更新。 这是通过检查InvokeRequired属性来完成的。 这是更新的逻辑的示例:

C#

C#

public partial class Form1 : System.Windows.Forms.Form
{
    private delegate void MyUpdaterDelegate(string value);

    public Form1()
    {
        InitializeComponent();
    }

    private void startButton_Click(object sender, System.EventArgs e)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(RunCustomLogic), "Done");
    }

    private void RunCustomLogic(object o)
    {
        for (int i = 0; i < 100; i++)
        {
            System.Threading.Thread.Sleep(10);
        }

        UpdateResultsBox(o.ToString());
    }

    private void UpdateResultsBox(string value)
    {
        if (this.resultsBox.InvokeRequired)
        {
            MyUpdaterDelegate del = UpdateResultsBox;

            this.resultsBox.Invoke(del, value);
        }
        else
        {
            this.resultsBox.Text = value;
        }
    }
}

VB

VB

Public Class Form1
    Private Delegate Sub MyUpdaterDelegate(ByVal value As String)

    Private Sub startButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles startButton.Click
        System.Threading.ThreadPool.QueueUserWorkItem(New System.Threading.WaitCallback(AddressOf RunCustomLogic), "Done")
    End Sub

    Sub RunCustomLogic(ByVal o As Object)
        For i As Integer = 0 To 99
            System.Threading.Thread.Sleep(10)
        Next

        UpdateResultBox(o.ToString())
    End Sub

    Sub UpdateResultBox(ByVal value As String)
        If Me.resultsBox.InvokeRequired Then
            Dim del As MyUpdaterDelegate = AddressOf UpdateResultBox

            Me.resultsBox.Invoke(del, value)
        Else
            Me.resultsBox.Text = value
        End If
    End Sub
End Class

The example resolution above is one way to defeat the InvalidOpertionException you encounter when you try to update GUI components from an external thread. I'm sure there are other ways to handle this, but this is what I am most familiar with.

上面的示例解决方案是克服尝试从外部线程更新GUI组件时遇到的InvalidOpertionException的一种方法。 我确定还有其他方法可以解决此问题,但这是我最熟悉的方法。

5.“第一次机会”例外 (5. 'First Chance' Exception)

While not an exception in and of itself, a "first chance" exception seems to cause a bit of confustion to new programmers and programmers who have never noticed it. Please note: There is no exception called "FirstChanceException."

虽然不是一个例外,但“第一次机会”例外似乎会给新程序员和从未注意到它的程序员带来一些混乱。 请注意:

So what is the big deal with a first chance exception anyway? As I said, there is no FirstChanceException class; instead of getting the "friendly" exception dialog you are accustomed to seeing, first chance exceptions are displayed in the Output window of Visual Studio. You will notice inside the message that a specific type of exception is mentioned, and this is the actual exception that was raised. The fact that it is "first chance" just means that the debugger saw the exception, and it was handled by some underlying layer. You are being notified of this action. The majority of the time, your application will not be adversely affected by the exception which was raised. I have not personally encountered any situations where a first chance exception resulted in my application not functioning, so i cannot convey to you the circumstances under which such a resulting behavior would occur. Just know that 99% of the time, a first chance exception can be ignored.

那么,无论如何有第一次机会例外的大问题是什么? 就像我说的,没有FirstChanceException类。 而不是习惯于看到“友好”异常对话框,而是在Visual Studio的“输出”窗口中显示首次机会异常。 您会在消息中注意到提到了特定类型的异常,这是引发的实际异常。 它是“第一次机会”的事实仅表示调试器看到了异常,并且该异常已由某些底层处理。 您将收到此操作的通知。 在大多数情况下,所引发的异常不会对您的应用程序产生不利影响。 我个人没有遇到过因偶然机会导致我的应用程序无法正常运行的任何情况,因此我无法向您传达发生这种情况的情况。 只需知道在99%的时间里,第一次机会异常可以忽略。

For more information on first chance exceptions (and second chance exceptions), see the this MS support article or this blog post from David Kline.

有关第一次机会例外(和第二次机会例外)的更多信息,请参阅这篇MS支持文章或David Kline的这篇博客文章 。

摘要 (Summary)

当您在编程中变得更“经验丰富”时,回头查看此列表可能会导致您轻笑。 上面列出的例外都是不常见的,在我看来,发生例外的情况都非常容易预防和/或纠正。 我的希望是,本文将帮助您识别会导致上述异常的常见问题。 当涉及到.NET中的异常时,某些异常类可以使程序员充分了解出了什么问题; 其他人可能会成为“国家宝藏3”中的藏宝图的明星。 无论您在代码中遇到什么特殊情况,似乎都无法识别出原因,请从类的名称开始,然后推断类试图告诉您什么。 然后尝试推断异常的错误消息。 如果您仍然挠头,请不要害怕在EE上进行网络搜索或出现有关异常的问题-可以肯定其他人已经挠挠了自己的头!

We all hate encountering exceptions in our code, but the only way to prevent them is to be aware of the conditions and scenarios that set the stage for them. Always aim for exceptional code  = )

我们都讨厌在代码中遇到异常,但是防止异常的唯一方法是了解为它们奠定基础的条件和场景。 始终以特殊代码为目标=)

翻译自: https://www.experts-exchange.com/articles/5389/5-Common-Exceptions-in-NET-and-How-to-Resolve-Them.html

17.常见的异常类有哪些?

你可能感兴趣的:(17.常见的异常类有哪些?_.NET中的5个常见异常(以及如何解决它们))