If I have the following code:
private void Check(bool a, bool b)
{
}
private void Check(int a, int b, int c, bool flag)
{
Check(a < b, a > (flag ? c : b - 10));
}
I get a compile-time error on the call to Check(int, int)
:
error CS0307: The variable 'int' cannot be used with type arguments
I also get these errors:
error CS0118: 'b' is a variable but is used like a type
error CS0118: 'a' is a variable but is used like a type
Why do these errors occur? What is wrong with the code?
This is a great day for me. I never thought I would see one of these in the wild! I have only ever seen this in compiler test cases.
Consider the following program fragment:
F(G<A,B>(7));
In C# 1.0, that meant "call method F with two arguments: G<A
and B>(7)
.
But C# 2.0 added generics. In C# 2.0 this means "call method F with one argument. The argument is a call to generic method G<A, B>
with one argument, 7".
This was a breaking change. C# has some heuristics to try to ensure that old programs that match this pattern keep working, but not all of them do.
C# is interpreting your program
Check(a < b, a > (flag ? c : b - 10));
as a call to Check
with one argument: a call to generic method a<b, a>
with one argument.
The solution for you is simple, as you have discovered: simply put in more parentheses to separate the arguments to Check.
If you are interested to know the exact rule C# uses to try to tell when it is generic and when it is not, it is:
If a sequence of tokens can be parsed as a simple-name, member-access, or pointer-member-access ending with a type-argument-list, the token immediately following the closing > token is examined. If it is one of ( ) ] } : ; , . ? == != | ^
then the type-argument-list is retained as part of the simple-name, member-access or pointer-member-access and any other possible parse of the sequence of tokens is discarded. Otherwise, the type-argument-list is not considered to be part of the simple-name, member-access or pointer-member-access, even if there is no other possible parse of the sequence of tokens. Note that these rules are not applied when parsing a type-argument-list in a namespace-or-type-name.
Having to implement rules like this makes C# a little bit tricky to parse, believe me.