Почему этот (null ||! TryParse) условный результат в "использовании неназначенной локальной переменной"?

Следующий код приводит к использованию неназначенной локальной переменной "numberOfGroups":

int numberOfGroups;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

Однако этот код отлично работает (хотя ReSharper говорит, что = 10 является избыточным):

int numberOfGroups = 10;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
    numberOfGroups = 10;
}

Мне что-то не хватает или компилятор мне не нравится ||?

Я сузил это до dynamic, вызывая проблемы (options была динамической переменной в моем вышеприведенном коде). Вопрос все еще остается, почему я не могу это сделать?

Этот код не компилируется:

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        dynamic myString = args[0];

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

Однако этот код делает:

internal class Program
{
    #region Static Methods

    private static void Main(string[] args)
    {
        var myString = args[0]; // var would be string

        int myInt;
        if(myString == null || !int.TryParse(myString, out myInt))
        {
            myInt = 10;
        }

        Console.WriteLine(myInt);
    }

    #endregion
}

Я не понимал, что dynamic будет фактором в этом.

Ответ 1

Я уверен, что это ошибка компилятора. Приятно найти!

Изменить: это не ошибка, как демонстрирует Quartermeister; динамический может реализовать странный оператор true, который может привести к тому, что y никогда не будет инициализирован.

Здесь минимальное воспроизведение:

class Program
{
    static bool M(out int x) 
    { 
        x = 123; 
        return true; 
    }
    static int N(dynamic d)
    {
        int y;
        if(d || M(out y))
            y = 10;
        return y; 
    }
}

Я не вижу причин, почему это должно быть незаконным; если вы замените dynamic на bool, он просто компилируется.

Я на самом деле встречаюсь с командой С# завтра; Я расскажу им об этом. Извините за ошибку!

Ответ 2

Возможно, что переменная не назначена, если значение динамического выражения имеет тип с перегруженным оператором true.

Оператор || вызывает оператор true, чтобы решить, следует ли оценивать правую часть, а затем оператор if будет вызывать оператор true, чтобы решить, следует ли оценивать его тело. Для нормального bool они всегда будут возвращать один и тот же результат, и точно один будет оцениваться, но для пользовательского оператора такой гарантии нет!

Создание Эрика Липперта - это краткая и полная программа, которая демонстрирует случай, когда ни один из путей не будет выполнен, а переменная будет иметь свое начальное значение:

using System;

class Program
{
    static bool M(out int x)
    {
        x = 123;
        return true;
    }

    static int N(dynamic d)
    {
        int y = 3;
        if (d || M(out y))
            y = 10;
        return y;
    }

    static void Main(string[] args)
    {
        var result = N(new EvilBool());
        // Prints 3!
        Console.WriteLine(result);
    }
}

class EvilBool
{
    private bool value;

    public static bool operator true(EvilBool b)
    {
        // Return true the first time this is called
        // and false the second time
        b.value = !b.value;
        return b.value;
    }

    public static bool operator false(EvilBool b)
    {
        throw new NotImplementedException();
    }
}

Ответ 3

Из MSDN (выделено мной):

Динамический тип включает операции, в которых происходит проверка обхода времени компиляции. Вместо этого эти операции разрешены во время выполнения. Динамический тип упрощает доступ к COM-интерфейсам API, таким как API автоматизации Office, а также к динамическим API-интерфейсам, таким как библиотеки IronPython, и к объектной модели документа HTML (DOM).

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

Поскольку компилятор не вводит проверку или разрешение любых операций, содержащих выражения типа dynamic, он не может гарантировать, что переменная будет назначена с помощью TryParse().