# 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.

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.

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:

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.

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.

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.

## 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:

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.

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:

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!

## 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.

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.

## 递归属性上的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:

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.

## 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:

C#

C＃

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

private void startButton_Click(object sender, System.EventArgs e)
{
}

private void RunCustomLogic(object o)
{
for (int i = 0; i < 100; i++)
{
}

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
End Sub

Sub RunCustomLogic(ByVal o As Object)
For i As Integer = 0 To 99
Next

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


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

InvalidOperationException抬起其丑陋的头，如下图所示：

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:

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)
{
}

private void RunCustomLogic(object o)
{
for (int i = 0; i < 100; i++)
{
}

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
End Sub

Sub RunCustomLogic(ByVal o As Object)
For i As Integer = 0 To 99
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.

## 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.

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

## 摘要 (Summary)

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

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

• 0
点赞
• 0
评论
• 0
收藏
• 一键三连
• 扫一扫，分享海报

08-13 3294
06-01 1万+

06-26 280
08-07 210
01-20 1746
07-15 75
05-30 2万+
09-18 8149
06-24 2493
05-12 1万+
08-12 8万+
04-18 4731
10-14 1088
11-30 7079
12-15 9万+