Method with two index parameters, where valid range interdependent: ArgumentOutOfRangeException OR ArgumentException?

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 and B; not exactly the valid range of A and B...
  • Ultimately, in this example, the valid range of B is defined by A; 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 than A.

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?

Jon Skeet
people
quotationmark

I'd go with ArgumentOutOfRangeException. That fits in with other examples elsewhere in the framework:

On the other hand, I should offer a counter-example too:

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 that x < y, then I'd say that y's valid range is defined in terms of x.

people

See more on this question at Stackoverflow