Syntax shortcut for calling generic methods - c#

I just found out that in C# the following will compile
public void Exec<T>(T t) => Console.WriteLine(t.ToString());
and you can call it like
Exec(1);
Exec(new SomeClass());
What I find interesting is that you can omit the parameter type in the brackets and still get type safety (if you added constraints to the T). Is this a new feature? This seems nifty

This is called generic type inference (see here). The compiler can see you are passing in an int or a SomeClass so it infers the generic type. Take this example:
public void DoStuff<T, U>(T param1, U, param2) { ... }
You can call it like this:
DoStuff(1, "hello");
In fact, you've probably already used this feature without even realising it. All of the Linq methods are generically typed, so any time you've used Where, Count or Select for example, you could have specified the types. Now can you imagine how ugly that code would look? For example:
var list = new List<string>();
var filteredList1 = list.Where<string>(s => s == "hello");
var filteredList2 = list.Where(s => s == "hello");
However, how about this:
public TOutput DoStuff<TInput, TOutput>(TInput param1) { ... }
If you call it without specifying the types, you are only allowing the compiler to infer the first generic type so you will get a compiler error:
CS0411 The type arguments for method 'DoStuff(TInput)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Related

Generic methods in C# not calling most restrictive overload

I have overloaded generic methods that look like this:
public static T DoSomething<T>(T val)
{
return val;
}
public static IEnumerable<T> DoSomething<T>(IEnumerable<T> vals)
{
return vals.Select(x => DoSomething(x));
}
The problem I am running into is that the following code won't compile:
var myList = new List<SomeObject>();
// This will not compile
PropertyOfTypeIEnumerable_SomeObject = MyStaticClass.DoSomething(myList);
The compiler is complaining that it can't convert SomeObject into IEnumerable<SomeObject>. This indicates that the compiler is choosing the first version of DoSomething, which takes generic type T, as opposed to the second version, which takes the more restrictive generic type IEnumerable.
I can confirm this is the case by renaming the second overloaded method to DoSomethingList and calling that name explicitly:
public static T DoSomething<T>(T val)
{
return val;
}
public static IEnumerable<T> DoSomethingList<T>(IEnumerable<T> vals)
{
return vals.Select(x => DoSomething(x));
}
// ...
var myList = new List<SomeObject>();
// This compiles ok
PropertyOfTypeIEnumerable_SomeObject = MyStaticClass.DoSomethingList(myList);
My question is, why doesn't the compiler choose the most restrictive matching generic implementation to call, and is there any way I can get that behavior without having to name it uniquely and call it explicitly? If the example were calling overloads for different objects which inherited from one another, then it would choose the most restrictive overload based on the declared variable type. Why doesn't it do this for generics as well?
Another option would be to not overload the methods, but to check inside of the DoSomething method to see if T is assignable from IEnumerable<>, but then I don't know how to actually cast it to something I could call the Linq method on, and I don't know how to get the compiler to be ok with returning the result of that Linq method, since I will know that the return result needs to be IEnumerable, but the compiler will not.
Generics in C# differ in many ways from C++ templates (1). You'd be right that it would call the more restrictive IEnumerable<T> function if we were working in C++ meta-programming. However, in C# the <T> function matches List<T> better, because it is more exact.
I tested your code above and it compiles just fine for me on .NET v4.0.30319 but it returns a List<T> type instead of the expected reduced IEnumerable<T> type that a Select call returns. Indicating that the <T> function was called.
If you want to perform DoSomething on all objects in IEnumerable extended classes, then here is a way to do so:
public static T DoSomething<T>(T val)
{
switch (val)
{
case IEnumerable vals:
foreach (object x in vals)
DoSomething(x);
break;
}
return val;
}
I've set it up in a way that allows for matching other specific types, as I'd guess that each different type is going to do something different. If that is not intended you can always use just a simple if...is statement matching.

Value of Generic Type when not Explicitly Passed?

In reviewing some code on github, I have come across this pattern:
using System.Linq.Expressions;
public T SomeGenericMethod<TValue>(Expression<Func<T, TValue>> myExpr){
// ...
}
This is the only relevant part of the code. The TValue never actually used in the method body, it is only present for use in the Func<,> type.
It's use then looks like this:
myObj.SomeGenericMethod(x => x.SomeProperty)
Note that no generic is passed on the call to SomeGenericMethod. I would have expected the compiler to require something like:
myObj.SomeGenericMethod<SomeTValue>(x => x.SomeProperty)
But it doesn't.
So my question is, what is TValue when nothing is explicitly passed as the type to the generic method invocation?
In this case, it is typeof(SomeProperty). The C# compiler will auto-discover it. From https://msdn.microsoft.com/en-us/library/twcad0zb.aspx
You can also omit the type argument and the compiler will infer it.
The compiler can infer the type parameters based on the method arguments you pass in; it cannot infer the type parameters only from a constraint or return value. Therefore type inference does not work with methods that have no parameters. Type inference occurs at compile time before the compiler tries to resolve overloaded method signatures. The compiler applies type inference logic to all generic methods that share the same name. In the overload resolution step, the compiler includes only those generic methods on which type inference succeeded.
Note that you can think that that usage is strange, but when you use LINQ, you do it normally. With Line you write:
var coll = new[] { new { Foo = 1, Bar = 2 } };
var enu = coll.Select(x => x.Foo).ToList();
You don't explicitly say anywhere the type of Foo in the Select. The compiler deducts it is an int.
The signature of the Select is:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
So the type of Foo is the TResult
Without this type inference, the anonymous objects would be nearly useless, because you couldn't:
class MyClass
{
public int Foo { get; set; }
public int Bar { get; set; }
}
var coll = new[] { new MyClass { Foo = 1, Bar = 2 } };
var enu = coll.Select<MyClass, ???>(x => new { Bar = x.Foo }).ToList();
What would you put in the ???? By definition of anonymous objects, you can't explicitly name them :-)
You can get away with not explicitly passing generics when the compiler can perform generic inferencing, wherein it will use the type of the arguments passed to determine the generic version to call upon. Note that inference happens at compile time, and thus can have some strange side effects with polymorphism.
In order to support the T value, this method would need to be within a class which has that T as a parameter. Because the class itself is compiled once per argument, any constituent members (including Subclasses!) can use that generic parameter, and this includes using it for the parameter of an argument, or the generic parameter of a further generic call.

Pass arbitrary actions to the same method

Why is the following call ambiguous:
public class Foo
{
public void Bar<T> (Action<T> simple);
public void Bar<T1, T2> (Action<T1, T2> complex);
}
...
public class Test
{
public static void MyComplex (string a, string b) { ... }
}
...
foo.Bar(Test.MyComplex);
Shouldn't it be clear to the compiler to call the Bar<T1,T2>() method?
If you remove this method public void Bar<T> (Action<T> simple);, your code just will not compile, because you get this exception:
The type arguments for method 'Foo.Bar(System.Action)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
Unfortunately the compiler can not get types from this method, and you should write this code to call method:
new Foo().Bar(new Action<string, string>(Test.MyComplex));
The compiler is trying to infer the generic parameter types for Bar but to do that it needs to know all the argument types. The argument you have (Test.MyComplex) is actually method group, not a delegate so the compiler is also required to insert a conversion to a compatible delegate type. However it can't because it does not know the what delegate types to use because type inference on the method it needs to be compatible with has not completed yet. There's a chicken-and-egg problem and the compiler gives up saying the call is ambiguous. Eric Lippert points out in the comments to a very similar question that in simple cases like this, it could be worked out but at the expense of complicating the overload resolution rules.
Unfortunately you are required to do something that gives the compiler more information:
foo.Bar<string, string>(Test.MyComplex);
or
Action<string, string> action = Test.MyComplex;
foo.Bar(action);

C#: Why doesn't generic type inference work when there are multiple type arguments?

Here are two samples:
This works fine:
void Main()
{
var list = Queryable.ProjectTo(typeof(Projection));
}
public static class QueryableExtensions
{
public static ProjectionList<T> ProjectTo<T>(this IQueryable<T> queryable, Type projectionType)
{
return new ProjectionList<T>(queryable, projectionType);
}
}
This throws the following error:
Using the generic method
'QueryableExtensions.ProjectTo(System.Linq.IQueryable)'
requires 2 type arguments
void Main()
{
var list = Queryable.ProjectTo<Projection>();
}
public static class QueryableExtensions
{
public static ProjectionList<T, P> ProjectTo<T, P>(this IQueryable<T> queryable)
{
return new ProjectionList<T, P>(queryable);
}
}
Of course the first example requires 1 type argument, however the compiler can figure out what it is so I don't need to supply any. The second example requires 2 type arguments but the compiler knows what T is so why does it not only require the one that cannot be inferred?
For the record I am using the first example just fine in my code, but I like the syntax of the second much better and there may be a case where I would like to have the generic type of the projection. Is there any way to achieve this or am I barking up the wrong tree?
Thanks!
The issue is not the 2 parameters, but rather: from where would it infer it? Generic parameter inference only looks at parameters, and specifically does not consider return types. There is nothing in the parameters that would suggest P. It is required that either generic type inference provides all of them, or all of them are specified explicitly. Interestingly, there was once mention of "mumble-typing" which, as I interpret it (since it never got defined fully) would have allowed you to mix and match, like you want. Imagine:
blah.ProjectTo<?,SomeType>();
(the exact syntax doesn't matter since this language feature doesn't exist) - but it would mean "there are 2 genericmtype arguments; you (the compiler) figure out the first parameter; the second is SomeType".
That's because generic parameter inference works only with input parameters. In your second example the P parameter appears only at the return type, thus the inference cannot work. So when you write:
var list = Queryable.ProjectTo<Projection>();
T is Projection but what's P for you?
But even if you write:
ProjectionList<Projection, FooBar> list = Queryable.ProjectTo();
it still wouldn't work.

C# Type inference: How to properly define constraints on method?

Hi I am having hard time making C# type inference do what I want. I have a very specific situation where I have a lot of variables in the flavor of
ConcurrentDictionary<T, IDictionary<U, V> >
where T,U,V can be some random types like long, int or whatever.
I want to write the method that works with this types of variables - notably examines their histograms.
So I have written a method
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<T, U, V, W>(T dictionary) where T : ConcurrentDictionary<U, IDictionary<V, W>>
{
return dictionary.Select(p => p.Value.Count)
.GroupBy(p => p)
.Select(p => new Tuple<int, int>(p.Key, p.Count()))
.OrderBy(p => p.Item1);
}
But when I try to call it, C# gives me an error that it cannot infer the types.
For example on a variable of type
ConcurrentDictionary<int,IDictionary<int, int> > foo;
I get the error:
Error 118 The type arguments for method
'Auditor.AuditorHelpers.GetDictionaryHistogram(T)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
What did I do wrong?
Type inference infers from arguments to formal parameter types. No inferences are ever made on constraints because constraints are not a part of the signature of a method.
In your case type inference must always fail; type inference cannot possibly infer types for U and V because they do not appear in a formal parameter type.
For about a dozen people telling me that I am wrong to believe that this rule is sensible, see the comments to my article on the subject.
The type parameter T is unnecessary. You should just write
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<U, V, W>(
ConcurrentDictionary<U, IDictionary<V, W>> dictionary)
and it should work fine.
Your type of constraint, where a parameter must be of a certain type, is useful only in a few situations. The most common one is to avoid boxing of value types that implement an interface. Compare these two methods:
public static DoSomething(ISomeInterface thing) {}
public static DoSomething<T>(T thing) where T : ISomeInterface {}
When the first method is called with a value type argument, the argument is boxed and cast to the interface type.
When the second method is called with a value type, the generic type parameter is replaced with the value type and no boxing takes place.
I think the compiler is saying that it can't figure out the dependencies between your types.
Try doing:
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<U, V, W>(ConcurrentDictionary<U, IDictionary<V, W>> dictionary)
{
return dictionary.Select(p => p.Value.Count)
.GroupBy(p => p)
.Select(p => new Tuple<int, int>(p.Key, p.Count()))
.OrderBy(p => p.Item1);
}

Categories