Gra*_*ham 2 c# generics fluent-interface
我正在编写一个静态防护类/ api来验证发送给方法的参数.
到目前为止的代码如下:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
它可以这样使用:
public void Test(IFoo foo) {
Guard.Ensure(foo, "foo").IsNotNull();
}
Run Code Online (Sandbox Code Playgroud)
现在的情况要求我需要从提供的界面转换为具体类型.不要问为什么,我只需要!
我想添加一个As扩展方法GuardArgument来执行此操作,例如:
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);
}
Run Code Online (Sandbox Code Playgroud)
我不太喜欢这种语法.我希望能够使用如下类:
Foo foo = Guard.Ensure(foo, "foo")
.As(typeof(Foo))
.IsNotNull()
.Value();
Run Code Online (Sandbox Code Playgroud)
我不知道如何编写扩展方法来允许这种语法. 我意识到我可以使用现有的流畅API:
Foo foo = Guard.Ensure(foo as Foo, "foo")
.IsNotNull()
.Value();
Run Code Online (Sandbox Code Playgroud)
但从可读性的角度来看,我不喜欢这个.
你可以得到这样的语法:
Foo foo = Guard.Ensure(foo, "foo")
.As<Foo>()
.IsNotNull()
.Value();
Run Code Online (Sandbox Code Playgroud)
诀窍是抛弃TIn类型参数.As()由于无法使用类型推断,因此不会在方法中使用它并使API膨胀TOut.为了能够在不获得As()所有类型建议的情况下执行此操作,您必须为您的GuardArgument<>类实现新的非泛型接口:
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.
}
Run Code Online (Sandbox Code Playgroud)
现在,您As()只需使用一个通用参数即可编写该方法:
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);
}
Run Code Online (Sandbox Code Playgroud)