I'm writing a simple set/framework of extension methods for argument/parameter validation. What I'm mostly concerned about, ATM is the performance, against the simpliest boilerplate
if(obj == null)
throw new ArgumentNullException()
The framework I came up with is like that: The Validatable class packing the base object that will be validated through the ThrowIf() extension method.
public class Validatable<T>
{
public T Value { get; }
public Validatable(ref T argument)
{
Value = argument;
}
}
public static Validatable<T> ThrowIf<T>(this T argument)
{
return new Validatable<T>(ref argument);
}
And test methods themselves:
public static Validatable<T> IsNull<T>(this Validatable<T> argument) where T : class
{
if (argument.Value == null)
throw new ArgumentNullException();
return argument;
}
public static Validatable<T> CollectionEmpty<T>(this Validatable<T> argument) where T: ICollection
{
if (argument.Value.Count == 0)
throw new ArgumentException();
return argument;
}
This allows me to validate items with simple:
List<int> testList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
testList.ThrowIf().IsNull().CollectionEmpty();
Everything was fine untill I decided to test, how it fares against the simple boilerplate.
int iNumber = 1000000000;
Stopwatch sw1 = new Stopwatch();
Stopwatch sw2 = new Stopwatch();
sw1.Start();
for (int i = 0; i < iNumber; ++i)
{
if (testList == null)
throw new ArgumentNullException();
if (testList.Count == 0)
throw new ArgumentException();
}
sw1.Stop();
sw2.Start();
for (int i = 0; i < iNumber; ++i)
{
testList.ThrowIf().IsNull().CollectionEmpty();
}
sw2.Stop();
Console.WriteLine($"sw1: {sw1.ElapsedTicks} ticks");
Console.WriteLine($"sw2: {sw2.ElapsedTicks} ticks");
and the output was:
sw1: 10720019 ticks
sw2: 161726524 ticks
That's an order of magnitute difference. It was over 1,000,000,000 iterations, but still the difference is there.
I also tried with
public static void IsNullLite<T>(this T argument)
{
if (argument == null)
throw new ArgumentException();
}
public static void CollectionEmpty2Lite<T>(this T argument) where T : ICollection
{
if (argument.Count == 0)
throw new ArgumentException();
}
And it's result came somewhere in the middle with
sw3: 84165637 ticks
My questions are:
- Should I be concerned with the performance loss. It was +- 3sec vs 47secs on my PC, at 1,000,000,000 iterations. I do not plan to run it in such loops, over so many objects, but I was going to use it my game engine, where I have about 16ms per Update tick, to do much more than just validate some variables.
- Is there a better/faster way of doing it. Preferably, I'd like to pack the validation object into some sort of Validatable class, to not clutter the intelisense with dozens of validation methods.
- Is there some error on my part, in the way I tested it? I ran the tests in Debug mode with code optimalization turned off.