Compiler Error: “error CS0307: The variable 'i

2019-04-04 05:57发布

问题:

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?

回答1:

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.