Roslyn не удалось скомпилировать код


после того, как я перенес свой проект с 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'

что вызывает эту проблему? Можно ли это исправить с помощью настройки компилятора?

4 94

4 ответа:

что вызывает эту проблему?

похоже на ошибку компилятора для меня. По крайней мере, так было. Хотя чем decimal.TryParse(v, out a) и decimal.TryParse(v, out b) выражения вычисляются динамически, I ожидается компилятор все еще понимает, что к тому времени он достигает a <= b, и a и b определенно назначены. Даже с причудами, которые вы можете придумать в динамическом типировании, я ожидал бы только когда-либо оценить a <= b после оценки обоих TryParse звонки.

однако, оказывается, что через оператор и преобразование сложно, вполне возможно иметь выражение A && B && C, который оценивает A и C а не B - если вы достаточно хитры. Смотрите сообщение об ошибке Рослин для гениального примера Нила Гафтера.

что делает эту работу с dynamic еще сложнее-семантика, связанная с динамическими операндами, сложнее описать, потому что для выполнения разрешение перегрузки, вам нужно оценить операнды, чтобы узнать, какие типы участвуют, что может быть противоинтуитивным. Тем не менее, снова Нил придумал пример, который показывает, что ошибка компилятора требуется... это не ошибка, это ошибка исправить. Огромное количество похвал Нилу за доказательство этого.

можно ли это исправить с помощью настроек компилятора?

нет, но есть альтернативы, которые позволяют избежать ошибка.

во - первых, вы можете остановить его от динамического-если вы знаете, что вы будете использовать только строки, то вы можете использовать IEnumerable<string>или дайте переменную диапазона v тип string (т. е. from string v in array). Это был бы мой предпочтительный вариант.

если вы действительно нужно держать его динамическим, просто дайте b значение для начала:

decimal a, b = 0m;

это не принесет никакого вреда - мы знаем, что на самом деле ваш динамическая оценка не будет делать ничего сумасшедшего, так что вы все равно в конечном итоге присвоить значение b прежде чем использовать его, делая начальное значение несущественно.

кроме того, кажется, что добавление скобок тоже работает:

where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)

это изменяет точку, в которой запускаются различные части разрешения перегрузки, и, случается, делает компилятор счастливым.

здесь один проблема все еще остается-правила спецификации по определенному назначению с помощью && оператор должен быть уточнен, чтобы заявить, что они применяются только тогда, когда && оператор используется в "обычной" реализации с двумя bool операнды. Я постараюсь убедиться, что это исправлено для следующего стандарта ECMA.

похоже, что это ошибка или, по крайней мере, регрессия в компиляторе Roslyn. Для его отслеживания была подана следующая ошибка:

https://github.com/dotnet/roslyn/issues/4509

тем временем, Джон отличный ответ!--5--> есть несколько обходных путей.

поскольку я так усердно учился в отчете об ошибке, я попытаюсь объяснить это сам.


представьте себе,T - Это определенный пользователем тип с неявным приведением к bool что чередуется между false и true, начиная с false. Насколько известно компилятору,dynamic первый аргумент к первому && может оценить этот тип, поэтому он должен быть пессимистичным.

если, тогда, он позволил коду компилироваться, это может бывает:

  • когда динамическая связка оценивает первый &&, он делает следующее:
    • оцените первый аргумент
    • это T - неявно приведите его к bool.
    • это false, поэтому нам не нужно оценивать второй аргумент.
    • сделать результат && оценить в качестве первого аргумента. (Нет, не false по какой-то причине.)
  • когда динамический связыватель оценивает второй &&, он делает следующее:
    • оцените первый аргумент.
    • это T - неявно приведите его к bool.
    • это true, Так что оцените второй аргумент.
    • ... Вот дерьмо,b не назначена.

в терминах спецификации, короче говоря, существуют специальные правила "определенного назначения", которые позволяют нам говорить не только о том, является ли переменная "определенно назначенной" или "определенно не назначено", но также, если это " определенно назначено после false оператор " или " определенно назначен после true заявление".

они существуют так, что при работе с && и ||! и ?? и ?:) компилятор может проверить, могут ли переменные быть назначены в определенных ветвях сложного логического выражения.

однако, они работают только в то время как типы выражений остаются логическое. Когда часть из выражения является dynamic (или не булев статический тип) мы больше не можем достоверно сказать, что выражение true или false - в следующий раз мы бросили его в bool чтобы решить, какую ветку взять, он, возможно, передумал.


Update: это уже разрешить и документирована:

определенные правила присваивания, реализованные предыдущими компиляторами для динамических выражений, допускали некоторые случаи кода это может привести к чтению переменных, которые не назначены определенно. Смотрите https://github.com/dotnet/roslyn/issues/4509 за одно сообщение об этом.

...

из-за этой возможности компилятор не должен разрешать компиляцию этой программы, если val не имеет начального значения. Предыдущие версии компилятора (до VS2015) позволяли этой программе компилироваться, даже если val не имеет начального значения. Рослин теперь диагностирует эту попытку прочитать возможно, неинициализированная переменная.

Это не ошибка. См.https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 для примера того, как динамическое выражение этой формы может оставить такую переменную out неназначенной.