I'm implementing a fluent argument assertion library where the focus is in strong type checking on compile time. Intellisense should only show methods and extensions available for the asserted type.
I'm having problems resolving proper type arguments when creating an extension for IEnumerable.
Idea in the library is that you can call ThrowIf (or ThrowIfNot) on any type which will return you an assertion instance of type IAssertion:
public static IAssertion<T> ThrowIf<T>(this T t)
{
return new IfAssertion<T>(t);
}
Now I want to check against IEnumerable if it contains a specific item. There will be two overloads where one takes the object of type T as a parameter and the other takes a function where to do the evaluation:
public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, T2 item)
where T1 : IEnumerable<T2>
{
// assertion logic
return assertion.Value;
}
public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, Func<T2, bool> func)
where T1 : IEnumerable<T2>
{
// assertion logic
return assertion.Value;
}
Everything goes fine when using the overload taking an instance of the actual type. But the latter one with the function compiler cannot infer the type arguments properly unless cast is made:
var list = new List<string>();
list.ThrowIf().Contains("foo"); // compiles
list.ThrowIf().Contains((string s) => false); // compiles
list.ThrowIf().Contains(s => false); // does not compile
Is there any way I could make the compiler happy without doing the cast for the function parameter?
More implementation details can be found from here: https://bitbucket.org/mikalkai/argument-assertions/overview
Disclaimer: This answer is only valid if IAssertion
can be made covariant.
Assuming that IAssertion
is covariant, you don't necessarily need two generic type parameters T1
and T2
for the Contains
methods. Instead, you specify IEnumerable
in your interface directly and use only one generic type parameter like this:
public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, T item)
{
// assertion logic
return assertion.Value;
}
public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, Func<T, bool> func)
{
// assertion logic
return assertion.Value;
}
Then you can use the contains method like this:
var list = new List<string>();
list.ThrowIf().Contains("foo"); // compiles
list.ThrowIf().Contains((string s) => false); // compiles
list.ThrowIf().Contains(s => false); // compiles now too
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