После того, как я перенес свой проект с VS2013 на VS2015, проект больше не строится. Ошибка компиляции возникает в следующем операторе LINQ:
static void Main(string[] args)
{
decimal a, b;
IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
var result = (from v in array
where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
orderby decimal.Parse(v)
select v).ToArray();
}
Компилятор возвращает ошибку:
Ошибка CS0165 Использование неназначенной локальной переменной 'b'
Что вызывает эту проблему? Можно ли это исправить настройкой компилятора?
b
после его назначения черезout
параметр.out
аргументах. Вернет ли этоTryParse
значение, допускающее значение NULL (или эквивалентное).where (a = decimal.TryParse(v)).HasValue && (b = decimal.TryParse(v)).HasValue && a <= b
выглядит намного лучшеdecimal a, b; var q = decimal.TryParse((dynamic)"10", out a) && decimal.TryParse("15", out b) && a <= b;
. Я обнаружил ошибку Roslyn, поднимающую это.Ответы:
Мне кажется, это ошибка компилятора. По крайней мере, так оно и было. Хотя
decimal.TryParse(v, out a)
иdecimal.TryParse(v, out b)
выражения вычисляются динамически, я ожидал , что компилятор до сих пор понять , что к тому времени он достигаетa <= b
, какa
иb
определенно присвоенной. Даже с учетом странностей, которые могут возникнуть при динамической типизации, я бы ожидал, что когда-либо буду оценивать толькоa <= b
после оценки обоихTryParse
вызовов.Тем не менее, оказывается, что через оператор и преобразование сложного, это вполне возможно , чтобы иметь выражение ,
A && B && C
которое приводитсяA
и ,C
но неB
- если вы хитрая достаточно. См. Гениальный пример Нила Гафтера в отчете об ошибке Roslyn .Заставить эту работу работать
dynamic
еще сложнее - семантику, используемую, когда операнды являются динамическими, сложнее описать, потому что для выполнения разрешения перегрузки вам необходимо оценить операнды, чтобы выяснить, какие типы задействованы, что может быть нелогичным. Однако, опять - таки Нил придумал пример , который показывает , что требуется ошибка компилятора ... Это не ошибка, это ошибка исправления . Большое спасибо Нилу за доказательство этого.Нет, но есть альтернативы, позволяющие избежать ошибки.
Во-первых, вы можете сделать так, чтобы он не был динамическим - если вы знаете, что будете использовать только строки, вы можете использовать
IEnumerable<string>
или дать переменной диапазонаv
типstring
(т.е.from string v in array
). Это был бы мой предпочтительный вариант.Если вам действительно нужно сохранить динамику, просто укажите
b
значение для начала:Это не принесет никакого вреда - мы знаем, что на самом деле ваша динамическая оценка не сделает ничего сумасшедшего, поэтому вы все равно будете присваивать значение,
b
прежде чем использовать его, делая начальное значение несущественным.Вдобавок кажется, что добавление круглых скобок тоже работает:
Это меняет точку, в которой запускаются различные части разрешения перегрузки, и делает компилятор счастливым.
Остается еще одна проблема -
&&
необходимо уточнить правила спецификации по определенному назначению с оператором, чтобы указать, что они применяются только тогда, когда&&
оператор используется в его «обычной» реализации с двумяbool
операндами. Я постараюсь исправить это для следующего стандарта ECMA.источник
IEnumerable<string>
меня сработало применение или добавление скобок. Теперь компилятор строит без ошибок.decimal a, b = 0m;
может удалить ошибку, но тогдаa <= b
будет использоваться всегда0m
, поскольку выходное значение еще не вычислено.decimal? TryParseDecimal(string txt)
тоже может быть решениемb
может не быть назначена»; Я знаю, что это неверное рассуждение, но оно объясняет, почему круглые скобки исправляют это ...Это действительно похоже на ошибку или, по крайней мере, регресс в компиляторе Roslyn. Для его отслеживания была зарегистрирована следующая ошибка:
А пока у отличного ответа Джона есть пара обходных путей.
источник
Поскольку я так усердно изучал отчет об ошибке, я попытаюсь объяснить это сам.
Представьте, что
T
это некоторый определяемый пользователем тип с неявным приведением к,bool
которое чередуется междуfalse
иtrue
, начиная сfalse
. Насколько известно компилятору,dynamic
первый аргумент первого&&
может оценивать этот тип, поэтому он должен быть пессимистичным.Если тогда он позволит коду скомпилировать, это может произойти:
&&
, оно выполняет следующие действия:T
- неявно приведено кbool
.false
, поэтому нам не нужно оценивать второй аргумент.&&
оценки первым аргументом. (Нет,false
по какой-то причине.)&&
, оно выполняет следующие действия:T
- неявно приведено кbool
.true
так что оцените второй аргумент.b
не назначено.Короче говоря, существуют специальные правила «определенного присваивания», которые позволяют нам говорить не только о том, является ли переменная «определенно назначенной» или «определенно не назначенной», но также и «определенно назначена после
false
оператора» или «определенно назначена». назначается послеtrue
заявления ".Они существуют для того, чтобы при работе с
&&
и||
(и!
и??
и?:
) компилятор мог проверить, могут ли быть присвоены переменные в определенных ветвях сложного логического выражения.Однако они работают, только если типы выражений остаются логическими . Когда часть выражения является
dynamic
(или не-логическим статическим типом), мы больше не можем достоверно сказать, что выражение являетсяtrue
илиfalse
- в следующий раз, когда мы приведем его,bool
чтобы решить, какую ветвь выбрать, оно могло передумать.Обновление: теперь это решено и задокументировано :
источник
out
к методу, имеющемуref
. Он с радостью сделает это и назначит переменные, не изменяя значения.false
/true
вместо неявного оператора приведения? Локально, он будет вызыватьimplicit operator bool
на первый аргумент, затем вызвать второго операнда, вызовoperator false
на первый операнд, а затемimplicit operator bool
на первый операнд снова . Для меня это не имеет смысла, первый операнд должен сводиться к логическому разу, не так ли?dynamic
, связанный цепью&&
? Я видел, как это в основном идет (1) оценивает первый аргумент (2) использует неявное приведение, чтобы увидеть, могу ли я закоротить (3) я не могу, поэтому оцените второй аргумент (4), теперь я знаю оба типа, я может видеть лучше&&
это определяемый пользователем&
(5) оператор вызоваfalse
на первый аргумент , чтобы увидеть , если я могу короткого замыкания (6) я могу (потомуfalse
и неimplicit bool
согласен), так что результат является первым аргументом ... , а затем следующий&&
, (7) используйте неявное приведение, чтобы увидеть, могу ли я (снова) закоротить.Это не ошибка. См. Https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 для примера того, как динамическое выражение этой формы может оставить такую выходную переменную неназначенной.
источник