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