Wednesday, January 7, 2009

Exceptions 101: ArgumentOutOfRangeException

Today's exception is the ArgumentOutOfRangeException. This exception is thrown whenever an argument passed into a method is outside of the accepted range for that value, thus, this exception should only ever be thrown in code for data types that have an actual range. For example, while Int32 has a range, Point does not have a range. However, the property X on Point does have a range, and it's range is the same as the range between Int.MinValue an Int.MaxValue. By the very definition of "Range", some data types should be simply excluded from being the root of the cause for this error.


The most common reason for an ArgumentOutOfRangeException is when your code is trying to access an element of a collection by it's numerical index in the collection, but the index passed in is lower than the index of the first item or higher than the index of the last item. For instance, let's say I have the following list of strings:

As you can see, I have six items in this list, and the indexes for them range from 0 to 5. Now, let's say I take this array and make the following code with it:

using System;

using System.Collections.Generic;

 

class Program

{

    static void Main(string[] args)

    {

        List<string> list = new List<string>() 

        { "David", "Morton", "is", "the", "coolest", "guy" };

 

        for (int i = 0; i <= list.Count; i++)

            Console.WriteLine(list[i]);

 

        Console.ReadLine();

    }

}


Now I'm going to be getting an ArgumentOutOfRangeException. The reason for this, is that I've defined my loop incorrectly. The Count property of the list is 6, because there's six items in the list, but if you look at the index values above my items, you'll see that the indexes only go up to 5. Apparently, the indexer property for List<T> is zero-based, and not one-based, which means the first item in the list actually has the index of 0, while the last item has an index of 5.

Debugging this may look a little daunting at first, mainly because my call doesn't look like an actual method call in this situation. For starters, I'm not calling any "dot" methods, such as list.Count. Instead, I'm calling what's called an indexer property, which is parameterized. This special kind of property doesn't appear to have a name in C#, and instead, takes a parameter, in square brackets, and uses that parameter to return a value. Nevertheless, don't be fooled by the brackets instead of the parenthesis. Behind the scenes, list[i] is really calling a method that is part of the property, and that i value gets used.

Back to the issue at hand... now that you know that it's the [i] that's throwing the property (if you're not sure which piece of code is throwing the error, break down your line of code and enter it into the watch window. See the article on NullReferenceException for more on how to do that), you can step through the code to evaluate the indexer value as the for loop makes it's iterations.

First, add a break point on the offending line. You can do this by clicking in the gray vertical bar just between the text editor and the edge of the window. When you do that, you'll see a red dot appear, and the entire line will be highlighted in red.

Next, run your application. The application should stop executing on the line with the break point. Now you need to open the locals window. To do this, go to the Debug window, and click "Windows" followed by "Locals", or just press Ctrl-Alt-V, followed by L.

Now, press F5. You should see the value next to i turn red and the value should change. Keep doing this until you see the exception thrown. When you see the exception thrown, you'll notice that the value of i is 6. No exception was thrown when the value was 5, so now we've found our problem. We'll just change the <= in the for statement to a <. This solves the problem.

Hopefully today you've seen how to debug yet another exception. I'll have another for you friday.