A code sample:
interface IFoo { }
class FooImpl : IFoo { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IFoo
{
}
Can anybody explain, why this method call:
var value = new FooImpl[0];
Bar(value);
targets Bar<T>(T source) (and, hence, doesn't compile)?
Does compiler take into account type parameter constraints at all, when resolving overloads?
UPD.
To avoid confusion with arrays. This happens with any implementation of IEnumerable<T>, e.g.:
var value = new List<FooImpl>();
UPD 2.
#ken2k mentioned covariance.
But let's forget about FooImpl. This:
var value = new List<IFoo>();
Bar(value);
produces the same error.
I'm sure, that implicit conversion between List<IFoo> and IEnumerable<IFoo> exists, since I can easily write something like this:
static void SomeMethod(IEnumerable<IFoo> sequence) {}
and pass value into it:
SomeMethod(value);
Does compiler take into account type parameter constraints at all, when resolving overloads?
No, because generic constraints are not part of the function signature. You can verify this by adding a Bar overload that is identical except for the generic constraints:
interface IBar { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IBar
{
// fails to compile : Type ____ already defines a member called 'Bar' with the same parameter types
}
The reason your code doesn't compile is because the compiler chooses the "best" match based on the method signature, then tries to apply the generic constraints.
One possible reason why it doesn't is because this call would be ambiguous:
{suppose List<T> had an Add<T>(IEnumerable<T> source) method}
List<object> junk = new List<object>();
junk.Add(1); // OK
junk.Add("xyzzy") // OK
junk.Add(new [] {1, 2, 3, 4}); //ambiguous - do you intend to add the _array_ or the _contents_ of the array?
The obvious fix is to use a different name for the Bar method that takes a collection (as is done in the BCL with Add and AddRange.
EDIT: Ok, the reason why Bar<T>(T source) is selected over Bar<T>(IEnumerable<T> source) when passing an List is because of the "7.5.3.2 Better function member" section of the C# language reference. What it says is that when an overload resolution must occur, the arguments types are matched to the parameters types of the applicable function members (section 7.5.3.1) and the better function member is selected by the following set of rules:
• for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
• for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.
(PX being the parameter types of the first method, QX of the second one)
This rule is applied "after expansion and type argument substitution". Since type argument substitution will swap the Bar(T source) to Bar>(IList source), this method arguments will be a better match than the Bar(IEnumerable source) which needs a conversion.
I couldn't find a online version of the language reference, but you can read it here
EDIT: misunderstood the question initially, working on finding the correct answer in the c# language spec. Basically IIRC the method is selected by considering the most appropriate type, and if you don't cast your parameter to IEnumerable<> exactly, then the Bar<T>(T source) will match the parameter type exactly, just like in this sample:
public interface ITest { }
public class Test : ITest { }
private static void Main(string[] args)
{
test(new Test() ); // outputs "anything" because Test is matched to any type T before ITest
Console.ReadLine();
}
public static void test<T>(T anything)
{
Console.WriteLine("anything");
}
public static void test(ITest it)
{
Console.WriteLine("it");
}
Will link to it when found
Because the cast between an array and an enumerable must be explicit: this compiles
var value = new FooImpl[0].AsEnumerable();
Bar(value);
and so does this:
var value = new FooImpl[0] as IEnumerable<IFoo>;
Bar(value);
From the doc:
Starting with the .NET Framework 2.0, the Array class implements the
System.Collections.Generic.IList,
System.Collections.Generic.ICollection, and
System.Collections.Generic.IEnumerable generic interfaces. The
implementations are provided to arrays at run time, and as a result,
the generic interfaces do not appear in the declaration syntax for the
Array class.
So your compiler doesn't know that the array matches the signature for Bar, and you have to explicitly cast it
As of c# 7.3, generic constraints are now considered part of the method signature for the purpose of overload resolution. From What's new in C# 7.0 through C# 7.3: Improved overload candidates:
When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
Thus in c# 7.3 / .Net Core 2.x and later, the code shown in the question compiles and runs successfully, and Bar<T>(IEnumerable<T> value) is called as desired.
A demo .Net 5 fiddle here now compiles successfully
A demo .NET Framework 4.7.3 fiddle here still fails to compile with the error:
The type 'FooImpl[]' cannot be used as type parameter 'T' in the generic type or method 'TestClass.Bar<T>(T)'. There is no implicit reference conversion from 'FooImpl[]' to 'IFoo'.
That's a covariance issue. List<T> is not covariant, so there is no implicit conversion between List<FooImpl> and List<IFoo>.
On the other hand, starting from C# 4, IEnumerable<T> now supports covariance, so this works:
var value = Enumerable.Empty<FooImpl>();
Bar(value);
var value = new List<FooImpl>().AsEnumerable();
Bar(value);
var value = new List<FooImpl>();
Bar((IEnumerable<IFoo>)value);
Related
Please have a look at the following code. Why do I get a compile-error?
I don't get it!
Casting is a way of telling the compiler that I know more about the objects than it does. And in this case, I know for fact, that "x" does actually contain an instance of "SomeClass". But the compiler seems to be unwilling to accept that information.
https://dotnetfiddle.net/0DlmXf
public class StrangeConversion
{
public class SomeClass { }
public interface ISomeInterface { }
public class Implementation : SomeClass, ISomeInterface { }
public void Foo<T>() where T : class
{
T x = (T)Factory();
//Compile-error: Cannot convert type 'T' to 'SomeClass'
SomeClass a = (SomeClass)x;
//This is perfectly fine:
SomeClass b = (SomeClass)(object)x;
if (x is SomeClass c)
{
//This works as well and 'c' contains the reference.
}
}
private object Factory()
{
return new Implementation();
}
}
Edit:
#Charles Mager has the correct answer in the comment: There does not seem to be a valid reason. The language designers just didn't want to allow this cast.
I fixed using the as casting e.g.
SomeClass a = x as SomeClass;
This Answer explains is very well https://stackoverflow.com/a/132467/16690008
Essentially it's because it would throw an exception if T is not type of that class
It's hard to make sense of exactly what you're trying to achieve, but it seems like a generic constraint is what you're after
public void Foo<T>()
where T : SomeClass // constrain T to be inheriting from SomeClass
{
T x = Factory<T>(); // x is guaranted to be SomeClass
}
private T Factory<T>()
where T : SomeClass // same here
{
return new Implementation();
}
You constrain the generic to only reference types by specifying where T : class, but the compiler needs to know with certainty if the cast is possible. This means you are asking the compiler to trust that SomeClass can be cast from any reference type you pass to it, which is something it won't do. The microsoft docs state that for the class generic type constraint:
The type argument must be a reference type. This constraint applies also to any class, interface, delegate, or array type.
Its important to note that SomeClass b = (SomeClass)(object)x; works because of the cast to object which is the root of the object hierarchy. But as you can see from the list of supported reference types, SomeClass a = (SomeClass)x; has to account for things such as delegates, array types, etc., at which point the compiler will throw you the error
Don't do SomeClass b = (SomeClass)(object)x;, it is much cleaner to make proper use of type constraints along with the as & is operators which were designed for this exact purpose of type checking and safe casting
Short answer:
This behaviour is correct according to the spec. The spec is just bad here since this might convert a compile-error into a runtime-error.
Long answer:
I did some more research on the matter. This is an oversight in the language's spec.
C# uses the same syntax for two totally different things:
int i = (int)1.9
This converts the double 1.9 to an integer. The value is actually changed.
object o = "abc";
string s = (string) o;
This looks the same, but does not change the object referenced by "o" at all. It does only convert the type of the reference.
When it comes to generics, this kind of ambiguity is an issue:
function f(T x) {
var x = (string) x;
}
What should the language do if T is "int"?
That's why the spec forces the developer to cast to object first:
function f(T x) {
var x = (string)(object)x;
}
Now, the behaviour is clear: X might still be a value-type. But if it is, it will be converted to a reference-type first.
This ambiguity does not exist in my example, since T is guaranteed to be a reference type:
public void Foo<T>() where T : class
Thus the cast to object is not necessary. It could even be harmful if the "where" specifies an actual type. In that case, the forced cast to object might convert a compile-time-error (impossible cast) to a runtime-error.
Unfortunately, the people who created the spec, did not see this issue and did not include it.
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.
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.
I encountered some unexpected compiler behaviour when calling overloaded method with different Action<T> variations.
Let's say I have this class Test and I'm creating its instance in the CallTest constructor.
public class Test
{
public Test(Action<long> arg)
{
}
public Test(Action<decimal> arg)
{
}
}
public class CallTest
{
public CallTest()
{
Test t = new Test(TestDecimal);
}
public void TestDecimal(decimal arg)
{
}
public void TestLong(long arg)
{
}
}
When calling the Test constructor with either TestDecimal or TestLong as a parameter I'm receiving the following error:
The call is ambiguous between the following methods or properties: 'Test(System.Action<long>)' and 'Test(System.Action<decimal>)'
My guess is there's some implicit conversion going on between long and decimal, but does anyone have any other idea what could have I done wrong? Is there any workaround?
When you pass TestDecimal or TestLong as a parameter you're in fact passing a method group (after all, there could be more than one TestDecimal method - it could have been overloaded). So in both cases implicit conversion occurs - from a method group to a particular delegate type. Both methods are thus applicable candidates (Section 7.4.2). From applicable candidates the overload resolution algorithm tries to find the best candidate. However, the rules of comparing conversions when matching parameter lists state, that if for both candidates implicit conversion occurs neither of them is better:
Section 7.4.2.3:
[...]
Otherwise, neither conversion is better.
That's why in your case there is an ambiguity.
The workaround is of course to first cast the parameter explicitly:
new Test(new Action<decimal>(TestDecimal))
This way for one case there will be no need for implicit conversion during overload resolution (as after the cast Action<T> type will match exactly), and the other would have to be converted (Action<long> to Action<decimal>), and the section mentioned above states that:
[...]
If S is T1, C1 is the better conversion.
If S is T2, C2 is the better conversion.
[...]
There is a workaround:
Test t = new Test(new Action<decimal>(TestDecimal));
This is due to an implicit casting between long and decimal.
Here's a table of implicit castings(for simple types) in C#(Picture Source):
Read more about type conversions here.
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);