Peculiar overload resolution with while (true)
So to start with, the first expression can only possibly call the first overload. It is not a valid expression for a Func<Task>
because there is a code path that returns an invalid value (void
instead of Task
).
() => while(true)
is actually a valid method for either signature. (It, along with implementations such as () => throw new Expression();
are valid bodies of methods that return any possible type, including void
, an interesting point of trivia, and why auto generated methods from an IDE typically just throw an exception; it'll compile regardless of the signature of the method.) A method that loops infinitely is a method in which there are no code paths that don't return the correct value (and that's true whether the "correct value" is void
, Task
, or literally anything else). This is of course because it never returns a value, and it does so in a way that the compiler can prove. (If it did so in a way that the compiler couldn't prove, as it hasn't solved the halting problem after all, then we'd be in the same boat as A
.)
So, for our infinite loop, which is better, given that both overload are applicable. This brings us to our betterness section of the C# specs.
If we go to section 7.4.3.3, bullet 4, we see:
If E is an anonymous function, T1 and T2 are delegate types or expression tree types with identical parameter lists, and an inferred return type X exists for E in the context of that parameter list (§7.4.2.11):
[...]
if T1 has a return type Y, and T2 is void returning, then C1 is the better conversion.
So when converting from an anonymous delegate, which is what we're doing, it will prefer the conversion that returns a value over one that is void
, so it chooses Func<Task>
.
Lambda conversions with unclear return type and overload resolution
I should've looked one section lower: §7.5.3.3 Better conversion from expression explains that:
Given an implicit conversion
C1
that converts from an expressionE
to a typeT1
, and an implicit conversionC2
that converts from an expressionE
to a typeT2
,C1
is a better conversion thanC2
if at least one of the following holds:
…
E
is an anonymous function,T1
is either a delegate typeD1
or an expression tree typeExpression<D1>
,T2
is either a delegate typeD2
or an expression tree typeExpression<D2>
and one of the following holds:
…
D1
andD2
have identical parameter lists, and one of the following holds:
…
D1
has a return typeY
, andD2
is void returning
Method overload resolution with regards to generics and IEnumerable
The first part of your question (without the List-specific overload) is easy. Let's consider the Array call, because it works the same for both calls:
First, type inference produces two possible generic implementations of the call: Print<Person[]>(Person[] items)
and Print<Person>(IEnumerable<Person> items)
.
Then overload resolution kicks in and the first one wins, because the second requires an implicit conversion, where the first one does not (see §7.4.2.3 of the C# spec). The same mechanism works for the List variant.
With the added overload, a third possible overload is generated with the List call: Print<Person>(List<Person> items)
. The argument is the same as with the Print<List<Person>>(List<Person> items)
but again, section 7.4.3.2 provides the resolution with the language
Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
So the Print<Person>
overload is more specific than the Print<List<Person>>
overload and the List version wins over the IEnumerable because it requires no implicit conversion.
Weird extension method overload resolution
Oh, I got it after re-reading my own answer! Nice question =)
The overload does not work because it does not take constraint where T:IMaker
into account while resolving overload (constraint is not a part of method signature). When you reference a parameter in lambda you (can) add a hint to the compiler:
This works:
new Container<A>().Foo(a => a.AProp == 0);
because here we do hint that a:A;
This does not work even with a reference to parameter:
new Container<A>().Foo(a => a != null);
because there is still not enough information to infer the type.
As far as I understand the specification, in "Foo scenario" the inference can fail on the second (Func) argument thus making the call ambiguous.
Here's what spec (25.6.4) says:
Type inference occurs as part of the compile-time processing of a method invocation (§14.5.5.1) and takes place before the overload resolution step of the invocation. When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. If type inference succeeds, then the inferred type arguments are used to determine the types of arguments for subsequent overload resolution.
If overload resolution chooses a generic method as the one to invoke, then the inferred type arguments are used as the runtime type arguments for the invocation. If type inference for a particular method fails, that method does not participate in overload resolution. The failure of type inference, in and of itself, does not cause a compile-time error. However, it often leads to a compile-time error when overload resolution then fails to find any applicable methods.
Now lets get to pretty straightforward "Bar scenario". After type inference we will get only one method, because only one is applicable:
Bar(Container<A>)
fornew Container<A>()
(does not implement IMaker)Bar(A)
fornew A()
(is not a Container)
And here's the ECMA-334 specification, just in case.
P.s. I'm not 100% sure that I got it right, but I prefer to think that I grasped the essential part.
Overload resolution error with DrawText
This doesn't seem to be a normal overload resolution situation. DrawText
is defined twice as :
function DrawText(hDC: HDC;
lpString: PWideChar;
nCount: Integer;
var lpRect: TRect;
uFormat: UINT): Integer; external user32 name 'DrawTextW';
function DrawText(hDC: HDC;
const lpString: UnicodeString;
nCount: Integer;
var lpRect: TRect; uFormat: UINT): Integer;
begin
Result := Winapi.Windows.DrawText(hDC,
PWideChar(lpString),
nCount,
lpRect,
uFormat);
end;
With {$TYPEDADDRESS OFF}
it seems that a ^Char
is interpreted by the compiler as an untyped pointer
that is never compatible with a declared type of PChar
while @c
does seem to resolve to a PChar
ok. This seems at odds with the notion that {$TYPEDADDRESS OFF}
is meant to make all pointers type-agnostic. It seems that PChar
and ^Char
are somehow treated differently in the compiler than other pointers.
With {$TYPEDADDRESS ON}
both @c
and ^Char
become equivalent, but curiously are accepted as arguments so long as there is no overload resolution to sort out.
In both cases it seems that overload resolutions are finalized before type-compatibility is fully established. I'm not sure I'd call it a bug, though... it seems like behaviour that would be tricky to change without causing problems.
SSCCE
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
{$DEFINE OVLD}
{$IFDEF OVLD}
procedure Test(s:string); overload;
begin
end;
{$ENDIF}
procedure Test(x:PChar); {$IFDEF OVLD}overload; {$ENDIF}
begin
end;
var
c : Char;
pc : ^Char;
begin
{$TYPEDADDRESS OFF}
Test(@c);
Test(pc); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - Incompat. types : 'PWideChar'-'pointer'
{$TYPEDADDRESS ON}
Test(@c); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - OK
Test(pc); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - OK
end.
C# method overload resolution issues in Visual Studio 2013
This is a fun one :) There are multiple aspects to it. To start with, let's simplify it very significantly by removing Rx and actual overload resolution from the picture. Overload resolution is handled at the very end of the answer.
Anonymous function to delegate conversions, and reachability
The difference here is whether the end-point of the lambda expression is reachable. If it is, then that lambda expression doesn't return anything, and the lambda expression can only be converted to a Func<Task>
. If the end-point of the lambda expression isn't reachable, then it can be converted to any Func<Task<T>>
.
The form of the while
statement makes a difference because of this part of the C# specification. (This is from the ECMA C# 5 standard; other versions may have slightly different wording for the same concept.)
The end point of a
while
statement is reachable if at least one of the following is true:
- The
while
statement contains a reachable break statement that exits the while statement.- The
while
statement is reachable and the Boolean expression does not have the constant valuetrue
.
When you have a while (true)
loop with no break
statements, neither bullet is true, so the end point of the while
statement (and therefore the lambda expression in your case) is not reachable.
Here's a short but complete example without any Rx involved:
using System;
using System.Threading.Tasks;
public class Test
{
static void Main()
{
// Valid
Func<Task> t1 = async () => { while(true); };
// Valid: end of lambda is unreachable, so it's fine to say
// it'll return an int when it gets to that end point.
Func<Task<int>> t2 = async () => { while(true); };
// Valid
Func<Task> t3 = async () => { while(false); };
// Invalid
Func<Task<int>> t4 = async () => { while(false); };
}
}
We can simplify even further by removing async from the equation. If we have a synchronous parameterless lambda expression with no return statements, that's always convertible to Action
, but it's also convertible to Func<T>
for any T
if the end of the lambda expression isn't reachable. Slight change to the above code:
using System;
public class Test
{
static void Main()
{
// Valid
Action t1 = () => { while(true); };
// Valid: end of lambda is unreachable, so it's fine to say
// it'll return an int when it gets to that end point.
Func<int> t2 = () => { while(true); };
// Valid
Action t3 = () => { while(false); };
// Invalid
Func<int> t4 = () => { while(false); };
}
}
We can look at this in a slightly different way by removing delegates and lambda expressions from the mix. Consider these methods:
void Method1()
{
while (true);
}
// Valid: end point is unreachable
int Method2()
{
while (true);
}
void Method3()
{
while (false);
}
// Invalid: end point is reachable
int Method4()
{
while (false);
}
Although the error method for Method4
is "not all code paths return a value" the way this is detected is "the end of the method is reachable". Now imagine those method bodies are lambda expressions trying to satisfy a delegate with the same signature as the method signature, and we're back to the second example...
Fun with overload resolution
As Panagiotis Kanavos noted, the original error around overload resolution isn't reproducible in Visual Studio 2017. So what's going on? Again, we don't actually need Rx involved to test this. But we can see some very odd behavior. Consider this:
using System;
using System.Threading.Tasks;
class Program
{
static void Foo(Func<Task> func) => Console.WriteLine("Foo1");
static void Foo(Func<Task<int>> func) => Console.WriteLine("Foo2");
static void Bar(Action action) => Console.WriteLine("Bar1");
static void Bar(Func<int> action) => Console.WriteLine("Bar2");
static void Main(string[] args)
{
Foo(async () => { while (true); });
Bar(() => { while (true) ; });
}
}
That issues a warning (no await operators) but it compiles with the C# 7 compiler. The output surprised me:
Foo1
Bar2
So the resolution for Foo
is determining that the conversion to Func<Task>
is better than the conversion to Func<Task<int>>
, whereas the resolution for Bar
is determining that the conversion to Func<int>
is better than the conversion to Action
. All the conversions are valid - if you comment out the Foo1
and Bar2
methods, it still compiles, but gives output of Foo2
, Bar1
.
With the C# 5 compiler, the Foo
call is ambiguous by the Bar
call resolves to Bar2
, just like with the C# 7 compiler.
With a bit more research, the synchronous form is specified in 12.6.4.4 of the ECMA C# 5 specification:
C1 is a better conversion than C2 if at least one of the following holds:
- ...
- E is an anonymous function, T1 is either a delegate type D1 or an expression tree type Expression, T2 is either a delegate type D2 or an expression tree type Expression and one of the following holds:
- D1 is a better conversion target than D2 (irrelevant for us)
- D1 and D2 have identical parameter lists, and one of the following holds:
- D1 has a return type Y1, and D2 has a return type Y2, an inferred return type X exists for E in the context of that parameter list (§12.6.3.13), and the conversion from X to Y1 is better than the conversion from X to Y2
- E is async, D1 has a return type
Task<Y1>
, and D2 has a return typeTask<Y2>
, an inferred return typeTask<X>
exists for E in the context of that parameter list (§12.6.3.13), and the conversion from X to Y1 is better than the conversion from X to Y2- D1 has a return type Y, and D2 is void returning
So that makes sense for the non-async case - and it also makes sense for how the C# 5 compiler isn't able to resolve the ambiguity, because those rules don't break the tie.
We don't have a full C# 6 or C# 7 specification yet, but there's a draft one available. Its overload resolution rules are expressed somewhat differently, and the change may be there somewhere.
If it's going to compile to anything though, I'd expect the Foo
overload accepting a Func<Task<int>>
to be chosen over the overload accepting Func<Task>
- because it's a more specific type. (There's a reference conversion from Func<Task<int>>
to Func<Task>
, but not vice versa.)
Note that the inferred return type of the lambda expression would just be Func<Task>
in both the C# 5 and draft C# 6 specifications.
Ultimately, overload resolution and type inference are really hard bits of the specification. This answer explains why the while(true)
loop makes a difference (because without it, the overload accepting a func returning a Task<T>
isn't even applicable) but I've reached the end of what I can work out about the choice the C# 7 compiler makes.
Very strange overload failure
What goes wrong?
Bar A({1,2});
Can be interpreted as:
Bar A(Bar{Foo<std::size_t>(1), (bool)2 /*, true*/ });
or
Bar A(Foo<std::size_t>{1,2} /*, true, true*/);
so ambiguous call.
How can I solve it?
It depends of which result you expect, adding explicit
might help for example.
Making explicit Foo(size_t n)
would allow only:
Bar A(B{Foo<std::size_t>(1), (bool)2 /*, true*/ });
Related Topics
Read Big Txt File, Out of Memory Exception
C# - Which Is the Best Alternative to 'Switch on Type'
How to Delete a Row from Gridview
Digital Signature in C# Without Using Bouncycastle
How to Simulate a Mouse Click at a Certain Position on the Screen
How to Know What Image Format I Get from a Stream
How Would I Sort a List of Files by Name to Match How Windows Explorer Displays Them
Testing Smtp Server Is Running via C#
Case Insensitive Regex Without Using Regexoptions Enumeration
How to Iterate Through a Datatable
Transparent Window Layer That Is Click-Through and Always Stays on Top
The Imported Project "C:\Microsoft.Csharp.Targets" Was Not Found
.Net Configuration (App.Config/Web.Config/Settings.Settings)
How to Reuse Code for Selecting a Custom Dto Object for a Child Property with Ef Core
How to Login/Authenticate a User with ASP.NET MVC5 Rtm Bits Using Aspnet.Identity