5 Common Exceptions in .NET (and How to Resolve Them)

kaufmed
CERTIFIED EXPERT
Published:
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.

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

1. NullReferenceException
2. FormatException
3. StackOverflowException
4. InvalidOperationException
5. "First Chance" Exception

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#
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!";
                          }
                      }

Open in new window

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

Open in new window



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#
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; } }
                      }

Open in new window

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

Open in new window


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.
Debugger Display - C# Debugger Display - VB
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


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#
private void button1_Click(object sender, System.EventArgs e)
                      {
                          string test = "Hello World!";
                          int converted = Convert.ToInt32(test);
                      }

Open in new window

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

Open in new window


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.
Debugger Display - C# Debugger Display - VB
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#
string test = "Hello World!";
                      int converted;
                      
                      if (int.TryParse(test, out converted))
                      {
                          // Conversion succeeded!
                      }

Open in new window

VB
Dim test As String = "Hello World!"
                      Dim converted As Integer
                      
                      If Integer.TryParse(test, converted) Then
                          ' Conversion succeeded!
                      End If

Open in new window


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


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 on a Recursive Method

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

Open in new window

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

Open in new window

This example is an oversimplification--most likely, you will have more complex logic than this causing the error, but you should get an idea of what to look for. In the above example, you can see that "value", as defined by my logic, can never 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.)

In the following images, you can see the results of our flawed logic above.
Debugger Display - C# Debugger Display - VB

StackOverFlowException on a Recursive Property

Earlier, I said that properties could be recusively flawed. Here is a demonstration of that statement:

C#
class MyExampleClass
                      {
                          private string _somePrivateMember;
                      
                          public string MyProperty
                          {
                              get { return this.MyProperty; }
                              set { this.MyProperty = value; }
                          }
                      }

Open in new window

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

Open in new window

Did you catch it? The problem with the above is that within the property, the property itself is being referred to in the get and set operations. This results in an endless loop, and susequently an overlfow of the stack:
Debugger Display - C# Debugger Display - VB
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.

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


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#
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();
                          }
                      }

Open in new window

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

Open in new window

In the above code, RunCustomLogic represents the logic I have forked off to another thread via the ThreadPool. I have used a "for" loop to simulate a lengthy operation. You will notice after the "for" loop terminates, I am attempting to print the message passed as a parameter to RunCustomLogic. This is what causes InvalidOperationException to rear its ugly head, as seen in the following images:
Debugger Display - C# Debugger Display - VB
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#
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;
                              }
                          }
                      }

Open in new window

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

Open in new window


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

As you become more "seasoned" in your programming, looking back on this list may cause you to chuckle. The exceptions listed above are all faily common, and the circumstances under which they occur seem, to me, all too easy to prevent and/or correct. My hope is that this article will aid you in recognizing common issues which generate those exceptions listed above. When it comes to exceptions in .NET, some exception classes do an adequate job of letting the programmer know just what is wrong; others could probably be cast to star as the treasure map in "National Treasure 3." Whatever exceptional situation you encounter in your code that you just cannot seem to identify the cause, start with the name of the class and infer what it is trying to tell you. Then try to extrapolate the exception's error message. If you are still left scratching your head, don't be afraid to do a web search or as a question about the exception right here at EE--assuredly someone else has already finished scratching thier own head!

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  = )
2
8,946 Views
kaufmed
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.