Consider the following scenario:
A method expects two indexes to be determined as parameters, and one of them to be equal to or greater than the other, making the valid range interdependent.
For example, a method to get a sub-copy of an array, with begin and end indexes:
public static T[] Sub<T>(this T[] @this, int start, int end) {
//Somewhere within...
var newLength = end - start;
if (newLength < 0)
//Throw exception because end<start...
}
Now, here is the (admittedly kind of irrelevant, but still interesting [IMHO]) problem:
ArgumentOutOfRangeException
is defined as:
The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.
Which kinda fits the scenario, and kinda doesn't:
- The method defines that "index A must be less than index B", but that defines the relationship between
A
andB
; not exactly the valid range ofA
andB
... - Ultimately, in this example, the valid range of
B
is defined byA
; not (explicitly) by the method.
On the other hand, ArgumentException
is defined as:
The exception that is thrown when one of the arguments provided to a method is not valid.
Which, given consideration to my argument regarding how the valid range of B
is defined by A
, actually fits perfectly:
- One argument,
B
, is invalid when it's less thanA
.
And yet, the exception (regardless of which) is still based on an index, and directly tied to valid or invalid ranges if indexing...
So...
Should the more general ArgumentException
be thrown, since it's the combination of the indexes that makes them invalid?
Should the more specific ArgumentOutOfRangeException
be thrown, even though the "invalidity" here doesn't(?) quite match the intended use of the exception?
Or should SomethingElseEntirelyException
be thrown?
I'd go with
ArgumentOutOfRangeException
. That fits in with other examples elsewhere in the framework:string.Substring(int, int)
throwsAOORE
if "startIndex plus length indicates a position not within this instance."Array.Copy(Array, int, Array, int, int)
throwsAOORE
if "sourceIndex is less than the lower bound of the first dimension of sourceArray."DateTime(int, int, int)
throwsAOORE
if "day is less than 1 or greater than the number of days in month."On the other hand, I should offer a counter-example too:
Stream.Read(byte[], int, int)
throwsArgumentException
if "The sum of offset and count is larger than the buffer length."I'm not sure why the last one was defined that way, but I'd say it's reasonably for the method to define the range of valid values in terms of other parameters and the state of the object. I would personally try to define the range of valid values of a particular parameter in terms of earlier parameters though - so if you have
Foo(int x, int y)
with the requirement thatx < y
, then I'd say thaty
's valid range is defined in terms ofx
.