Ответ 1
Это отличный день для меня. Я никогда не думал, что увижу одну из них в дикой природе! Я только когда-либо видел это в тестах компилятора.
Рассмотрим следующий фрагмент программы:
F(G<A,B>(7));
В С# 1.0 это означало "метод вызова F с двумя аргументами: G<A
и B>(7)
.
Но С# 2.0 добавил дженерики. В С# 2.0 это означает "метод вызова F с одним аргументом. Аргумент - это вызов универсального метода G<A, B>
с одним аргументом, 7".
Это было потрясающее изменение. У С# есть несколько эвристик, чтобы попытаться сохранить старые программы, соответствующие этому шаблону, но не все из них.
С# интерпретирует вашу программу
Check(a < b, a > (flag ? c : b - 10));
как вызов Check
с одним аргументом: вызов универсального метода a<b, a>
с одним аргументом.
Решение для вас просто, как вы обнаружили: просто добавьте в круглые скобки дополнительные аргументы для проверки.
Если вам интересно узнать точное правило, которое использует С#, чтобы попытаться рассказать, когда оно является общим, а когда нет, это:
Если последовательность токенов может быть проанализирована как простое имя, член-доступ или конец доступа к указателю-члену с типом-аргументом-списком, проверяется токен, следующий за закрывающим маркером. Если это один из
( ) ] } : ; , . ? == != | ^
, список типов-аргументов сохраняется как часть простого имени, доступа к члену или указателю-члену, а любой другой возможный синтаксический разбор последовательности токенов отбрасывается. В противном случае список типов-аргументов не считается частью простого имени, доступа к члену или указателю-члену, даже если нет другого возможного анализа последовательности токенов. Обратите внимание, что эти правила не применяются при анализе списка типов-аргументов в пространстве имен имен или имен.
Для реализации правил, подобных этому, С# немного сложно разобрать, поверьте мне.