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
Related
I am trying to make an extension method more generic to avoid redundancy (Here is an example of some real code, the code below is just to demonstrate the issue - I had the idea to make the method available for IQueryable<T> as well).
The following works fine:
public static class Extensions
{
public static IEnumerable<T> MySelect1<T, V>(this IEnumerable<T> query, Func<T, V> f)
{
// do something, then return IEnumerable<T>
var result=query.AsEnumerable<T>();
return result;
}
public static IQueryable<T> MySelect1<T, V>(this IQueryable<T> query, Func<T, V> f)
{
// do something, then return IQueryable<T>
var result = query.AsQueryable<T>();
return result;
}
}
I can use it in LinqPad like (when connected with the Northwind sample database):
var myQuery=(from x in Customers select x);
myQuery.AsEnumerable().MySelect1(d => d.CustomerID).Dump();
myQuery.AsQueryable().MySelect1(d => d.CustomerID).Dump();
Now I wanted to get rid of the duplicate implementation of MySelect1, so I refactored it as:
public static class Extensions
{
public static E MySelect2<E, T, V>(this E query, Func<T, V> f)
where E : System.Linq.IQueryable<T>, System.Collections.Generic.IEnumerable<T>
{
return (E)query.Select(f);
}
}
This compiles too, but I cannot use MySelect2 the same way as I did above, consider the following:
// CS0411 The type arguments for method 'Extensions.MySelect2<E, T, V>(E, Func<T, V>)'
// cannot be inferred from the usage. Try specifying the type arguments explicitly.
myQuery.AsEnumerable().MySelect2(d => d.CustomerID).Dump();
myQuery.AsQueryable().MySelect2(d => d.CustomerID).Dump();
Ok, doing what the error asks for works for this code line:
myQuery.AsQueryable()
.MySelect2<IQueryable<Customers>, Customers, String>(d => d.CustomerID).Dump();
but not for that one:
myQuery.AsEnumerable<Customers>()
.MySelect2<IEnumerable<Customers>, Customers, String>(d => d.CustomerID).Dump();
Here, I am getting
CS0311 The type 'System.Collections.Generic.IEnumerable<LINQPad.User.Customers>' cannot be used as type parameter 'E' in the generic type or method 'Extensions.MySelect2<E, T, V>(E, Func<T, V>)'. There is no implicit reference conversion from 'System.Collections.Generic.IEnumerable<LINQPad.User.Customers>' to 'System.Linq.IQueryable<LINQPad.User.Customers>'.
Why? And how can it be fixed? Please help.
Why?
For exactly the reason stated in the error message: you're trying to use IEnumerable<Customers> as the type argument for E, but E has this constraint:
where E : System.Linq.IQueryable<T>
And how can it be fixed?
It can't, assuming I understand what you're trying to achieve.
There's a fundamental problem with the "simplification" you're trying to achieve: you don't actually have full duplication in your original MySelect1 methods. The first calls AsEnumerable() and the second calls AsQueryable(). You're trying to replace those with a cast, and that's just not going to work.
There's a further problem, even with your original methods: you're accepting Func<T, V> f as a parameter for your queryable-based method, which means any time you call Select or similar and passing in f, you'll be calling Enumerable.Select instead of Queryable.Select. To really use IQueryable<> properly, you should accept Expression<Func<T, V>> f instead. At that point, you won't need to call AsQueryable anyway.
Your two methods "should" take radically different paths based on whether you're using LINQ to Objects or a different LINQ provider (e.g. LINQ to SQL), and that can't be hidden as a pure implementation detail without significant changes that would probably make it less useful than you want anyway.
I am trying to define a valid input for my method Pairwise. Pairwise takes an argument IEnumerable which I am having trouble figuring out what is exactly. I have tried alot of stuff but can never really get there.
public delegate void PairwiseDel(Type left, Type right);
public static void Pairwise(IEnumerable<Type> col, PairwiseDel del)
{
// stuff happens here which passes pairs from col to del
}
can someone plz tell and illustrate what a valid input for my method would be?
IEnumerable<T> is a very important interface in .NET library. It represents an abstraction describing a sequence of elements of type T.
This generic interface has multiple implementations:
Built-in 1-dimension arrays T[] implement IEnumerable<T>
All generic .NET collections implement IEnumerable<T>
Methods that use yield return produce IEnumerable<T>
Multiple methods in .NET LINQ library both take and return IEnumerable<T>
If you would like to test your method, pass it an array Type[]:
var items = new Type[] { typeof(int), typeof(string), typeof(long) };
Pairwise(items, (a, b) => {
Console.WriteLine("A={0}, B={1}", a.Name, b.Name);
});
This would be a valid input:
var collection = new List<Type>();
collection.Add(typeof(string));
collection.Add(typeof(int));
PairWise(collection, YourDelegateHere);
I'm trying to write a generic method that supplies parameters and calls a function, like this:
class MyClass {
public int Method(float arg) => 0;
}
TResult Call<T1, TResult>(Func<T1, TResult> func) =>
func(default(T1));
void Main()
{
var m = new MyClass();
var r1 = Call<float, int>(m.Method);
var r2 = Call(m.Method); // CS0411
}
The last line fails to compile with CS0411. Is there any workaround to get type inference to work here?
Use case: using AutoFixture to generate function call parameters.
Unfortunately no, that's a limitation of type inference in C#. It doesn't really do much with return types, which is what's needed in your case to fully infer the generic arguments to Call<>.
It is possible to create a Func object what references a generic method? like the LINQ OrderBy:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
If I understand you correctly, you're asking if you can reference a generic method from within an anonymous method.
The answer is yes.
For example, suppose you want some Func that returns the elements of an IEnumerable<int> object in sorted order (precisely like OrderBy<int, int>). You could do this:
Func<IEnumerable<int>, Func<int, int>, IOrderedEnumerable<int>> orderByFunc =
System.Linq.Enumerable.OrderBy<int, int>;
Then you could use this Func just like any other:
int[] ints = new int[] { 1, 3, 5, 4, 7, 2, 6, 9, 8 };
// here you're really calling OrderBy<int, int> --
// you've just stored its address in a variable of type Func<...>
foreach (int i in orderByFunc(ints, x => x))
Console.WriteLine(i);
Output:
1
2
3
4
5
6
7
8
9
On the other hand, if you're asking whether it's possible to create a "generic anonymous method," like this:
Func<T> getDefault<T> = () => default(T);
Then it depends on your context. This can be done from within a context where T is already declared as a generic type parameter -- namely, within a generic class or generic method. (See Freddy Rios's answer.) Outside of such a context, unfortunately, it is illegal.
Yes, but it depends on the context - if you are already working with generics, just use the T in the context / if not, then you already know the specific type. In the later, if you need to reuse a bit of logic on a method, u probably already would benefit of moving that into a method, so just do like my second example below.
2 samples:
public T Something<T>() {
Func<T> someFunc = () => { return default(T); };
return someFunc();
}
public Func<T> GetDefaultCreator<T>() {
return () => { return default(T); };
}
Something like this?
Func<Nullable<int>, string> myFunc = c => c.HasValue ? c.ToString() : "null";
That successfully compiles, and you could assign any function to that that takes in a Nullable and returns a string.
I have done something like this:
public static class Helper{
public static IEnumerable<KeyValuePair<string, string>> ToPairs(this NameValueCollection Form)
{
return Form.AllKeys.Cast<string>()
.Select(key => new KeyValuePair<string, string>(key, Form[key]));
}
}
Where this method has become an extension method to the request.form in C# web development.
I think I get it: Given the function static TResult DoSomeStuff<T, TResult>(T obj), can you create a Func<T, TResult> such that it will reference the function above, with no type parameters given at the creation of the reference to it.
I think this could work (You're welcome to test it, I have no C# near me at the moment):
class UselessClass<T, TResult>
{
// If it's a static method, this is fine:
public Func<T, TResult> DaFunc = RelevantClass.DoSomeStuff<T, TResult>;
// If not, something like this is needed:
public UselessClass(SomeClassWhereTheFunctionIs from)
{
DaFunc = from.DoSomeStuff<T, TResult>;
}
}
Also, in OrderBy, it's not actually a generic delegate. It's a declaration of a variable. When the function is given to it, the types are inferred from it.
Yes it's possible but you'll need to specify the type argument(s)
func<int> f = myClass.returnsT<int>;
where
class myClass
{
T returnsT<T>()
{...}
}
it Will not work without the type arguments
I have some example data:
public struct Task
{
public int INT;
public string STRING;
public DateTime? NULLABLEDATETIME;
}
And function, which uses it:
public KeyValuePair<Expression<Func<Task, object>>, object> Marry(Expression<Func<Task, object>> key, object value)
{
return new KeyValuePair<Expression<Func<Task, object>>, object>(key, value);
}
Here is example of function call:
Marry(t => t.INT, 1984);
Marry(t => t.NULLABLEDATETIME, DateTime.Now);
Marry(t => t.STRING, "SomeSting");
This code works, no questions. Unfortunatly we can also call functions like below, because String and int are both inherited from object class:
Marry(t => t.INT, "SomeSting");
I want to say compiler, that first and second parameters have same data type: int -> int, string -> string, DateTime? -> DateTime? and check it during compilation. I tried this:
public KeyValuePair<Expression<Func<Task, T1>>, T2> Marry<T1, T2>(Expression<Func<Task, T1>> key, T2 value)
where T2 : T1
{
return new KeyValuePair<Expression<Func<Task, T1>>, T2>(key, value);
}
This almost works, and if I try to put wrong data like this Marry(t => t.INT, "SomeSting"); the compiler reports an error:
The type 'string' cannot be used as type parameter 'T2' in the generic type or method 'Task.Marry(System.Linq.Expressions.Expression>, T2)'. There is no implicit reference conversion from 'string' to 'int'.
But this solution does not work with null. When I call Marry(t => t.NULLABLEDATETIME, null); the compiler says:
The type arguments for method 'Marry(System.Linq.Expressions.Expression>, T2)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Why? I already know the data type: DateTime?. I don't want to explicitly call Marry<DateTime?>(t => t.NULLABLEDATETIME, null);. How can I do this - or is there another way to check some function parameter types during compilation?
Why don't you simply replace T1 and T2 with a single generic type parameter? Then the second parameters type needn't be inferred - which it doesn't have to be anyhow since it must be identical to that of the first parameter.
That would look like this:
Variant 1
public KeyValuePair<Expression<Func<Task, T>>, T>
Marry<T>(
Expression<Func<Task, T>> key,
T value) {
return new KeyValuePair<Expression<Func<Task, T>>, T>(key, value);
}
I don't recommend this, but if you want the type-inference to "greedily" determine the type of the expression based solely on the first parameter (so that you get the error "cannot convert from 'string' to 'DateTime?'), you can curry the method:
Variant 2
public static Func<T, KeyValuePair<Expression<Func<Task, T>>, T> >
Marry<T>(
Expression<Func<Task, T>> key) {
return (value=>new KeyValuePair<Expression<Func<Task, T>>, T>(key, value));
}
//usage:
Marry(t => t.NULLABLEDATETIME)("test");
//error: Delegate 'System.Func<System.DateTime?,System.Collections.Generic.KeyValuePair<System.Linq.Expressions.Expression<System.Func<UserQuery.Task,System.DateTime?>>,System.DateTime?>>' has some invalid arguments
// - Argument 1: cannot convert from 'string' to 'System.DateTime?'
That error message essentially covers it. However, the API isn't too readable and the usage of heavily nested generic types makes things... less readable. A longer variant with essentially the same behavior but shorter error messages could look as follows:
Variant 3
public class MarriagePartner<T> {
readonly Expression<Func<Task, T>> key;
public MarriagePartner(Expression<Func<Task, T>> key) { this.key = key; }
public KeyValuePair<Expression<Func<Task, T>>, T> With(T value) {
return new KeyValuePair<Expression<Func<Task, T>>, T>(key, value);
}
}
public static MarriagePartner<T> Marry2<T>(Expression<Func<Task, T>> key) {
return new MarriagePartner<T>(key);
}
//Usage:
Marry2(t => t.NULLABLEDATETIME).With("test");
//error: The best overloaded method match for 'UserQuery.MarriagePartner<System.DateTime?>.With(System.DateTime?)' has some invalid arguments
// - Argument 1: cannot convert from 'string' to 'System.DateTime?'
That error message is a little more specific. I think Variant 1's error message suffices and it's what I'd use (KISS and all) - but if you Really want that casting error message, Variant 3 has the most understandable (if longer) implementation and most friendly error message.
You can cast null to some type
(string)null, (object)null