Examples of validating input parameters in Rotor

As I've been blog'ging lately - I'm a Rotor convert! There are so many great examples on how to create tight code in the Rotor source that it really does pay to use it to improve your own coding techniques. This week I've been noticing the input checking patterns that developers of the Rotor source seem to have followed where inputs are always checked at the top of routines before any processing or handling of data is done. In general the pattern looks like this:

public void Foo( arg0...argN ) {
    // check for null references
    // check for boundary errors
    // check for bad indexes or bad range data

    // Process data
    // return
}

 

Here's an actual example from a class in the IO namespace:

public override int Read([In, Out] byte[] buffer, int offset, int count) {
if (!_isOpen) __Error.StreamIsClosed(); if (buffer==null) throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); // Remainder of code...

 

Some other examples are...

Taking an object as an arg:

if ( buffer == null ) 
    throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));

if ( ownerDocument == null )
    throw new ArgumentException(Res.GetString(Res.Xdom_Node_Null_Doc));

 

Taking an Enum as an arg:

if (mode < FileMode.CreateNew || mode > FileMode.Append ||
    access < FileAccess.Read || access > FileAccess.ReadWrite ||
    share < FileShare.None || share > FileShare.ReadWrite) {
String badArg = "mode";


if (access < FileAccess.Read || access > FileAccess.ReadWrite) badArg = "access"; if (share < FileShare.None || share > FileShare.ReadWrite) badArg = "share";
throw new ArgumentOutOfRangeException(badArg, Environment.GetResourceString("ArgumentOutOfRange_Enum")); }

Taking a index, range or boundary test:

if ( capacity < 0 ) 
    throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));

if ((digits < 0) || (digits > maxRoundingDigits))
    throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));

if (value == SByte.MinValue)
    throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));

if (formats.Length == 0)
    throw new FormatException(Environment.GetResourceString("Format_BadFormatSpecifier"));

 

An exception to checking inputs at the top of methods is where routines are parsing for known formats. In this case exceptions are often handled at the bottom of routines after each possible format has been attempted. There are many examples of this in some of the DateTime classes.


Further reading:
Rotor Source

4 Comments

  • The rotor source is great. I'm having a great time reading through the System.Xml namespace.

  • Looking at the &quot;Taking an Enum as an arg&quot; example makes me wonder what the rules are for using InvalidEnumArgumentException as opposed to ArgumentOutOfRangeException? I would have expected that InvalidEnumArgumentException should always be used in this scenario.

  • You have to be extra careful when making note of this *feature* in the rotor source code. Microsoft has done numerous performance testing to determine which methods should keep these checks and which methods should be void of checks. Why? Well, because methods that are called extremely often and where the computational overhead of the method is very small, little things like argument checking and consume a good percentage of the time inside of the method. This leads to poor performance.



    You really need to ask yourself this question, &quot;What happens if the arguments are invalid?&quot;, and &quot;When this happens is that okay?&quot;.



    Most of the time you'll say that it throws a null reference exception and that the user can catch this exception in place of some other exception you are throwing. If there is an enumeration, you can simply supply a switch statement that has a default statement that will provide support for the most often used statement. There are many ways around this parameter checking at the top of the method and many times where removing this checking can be helpful.



    I also recommend providing debug/retail versions of the library with extended parameter checking in the debug build. As long as consumers of your application are aware that the retail build will throw differently, they can optimize their own code to make sure they aren't passing in bad parameters.



    Another metric or question is, &quot;Who is sending me my data?&quot;. If the answer is, I am within my own library, then you can remove checking in most cases (albeit private reflection does exist). If the answer is another developer, then you can only check for parameters that would cause security risks within your library. If the answer is that your library is expected to be provided parameters direct from a user, then you'll want full type checking, bounds checking, etc...



    While many people argue it is better to be safe than sorry, performance is still important, and every if check and every throw is killing someone's performance.

  • Hi Andrew, there is! It the category ambiguously named &quot;Rotor&quot; :P

Comments have been disabled for this content.