I am writing a static guard class/api to validate parameters sent to methods.
Code so far looks like:
public static class Guard
{
public static GuardArgument<T> Ensure<T>(T value, string argumentName)
{
return new GuardArgument<T>(value, argumentName);
}
public static T Value<T>(this GuardArgument<T> guardArgument)
{
return guardArgument.Value;
}
// Example extension method
public static GuardArgument<T> IsNotNull<T>(this GuardArgument<T> guardArgument, string errorMessage)
{
if (guardArgument.Value == null)
{
throw new ArgumentNullException(guardArgument.Name, errorMessage);
}
return guardArgument;
}
}
It can be used as so:
public void Test(IFoo foo) {
Guard.Ensure(foo, "foo").IsNotNull();
}
Circumstances now require that I need to cast to concrete types from a supplied interface. Don't ask why, I just need to!
I want to add an As extension method to GuardArgument to do this, something like:
public static GuardArgument<TOut> As<TOut, TIn>(this GuardArgument<TIn> guardArgument, Type type)
where TOut : class
{
// Check cast is OK, otherwise throw exception
return new GuardArgument<TOut>(guardArgument.Value as TOut, guardArgument.Name);
}
I don't much like the syntax though. I want to be able to use the class as follows:
Foo foo = Guard.Ensure(foo, "foo")
.As(typeof(Foo))
.IsNotNull()
.Value();
I'm not sure how to write the extension method to allow this syntax though. I realise I can use the existing fluent API as:
Foo foo = Guard.Ensure(foo as Foo, "foo")
.IsNotNull()
.Value();
but I don't like this from a readability perspective.
You can get this syntax:
Foo foo = Guard.Ensure(foo, "foo")
.As<Foo>()
.IsNotNull()
.Value();
The trick is to ditch the TIn type param. It's not used in the As() method and bloats the API when type inference can't be used due to TOut. To be able to do that without getting As() suggested on all types you have to implement a new, non-generic interface for your GuardArgument<> class:
interface IGuardArgument
{
object Value { get; }
strign Name { get; }
}
public class GuardArgument<T> : IGuardArgument
{
// Explicit implementation to hide this property from
// intellisense.
object IGuardArgument.Value { get { return Value; }
// Rest of class here, including public properties Value and Name.
}
Now you can write the As() method with only one generic param:
public static GuardArgument<TOut> As<TOut>(this IGuardArgument guardArgument)
where TOut : class
{
// Check cast is OK, otherwise throw exception
return new GuardArgument<TOut>(guardArgument.Value as TOut, guardArgument.Name);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With