Подтвердить что ты не робот

Почему компилятор жалуется, что "не все пути кода возвращают значение", когда я могу ясно видеть, что они делают?

Я пытаюсь понять, почему у компилятора есть проблемы с этой функцией. Это дает мне ошибку "Не все коды пути возвращают значение", однако я не вижу ситуации, когда поток управления передается выражению if( a ) без a, являющегося истинным (поэтому if( a ) является излишним, но компилятор, похоже, не признает этого).

public static Boolean Foo(Boolean x)
{
    Boolean a = false;
    if( x )
    {
        a = true;
    }
    else
    {
        try
        {
            SomethingThatMightThrow();
            Assert.IsFalse( a );
            return a;
        }
        catch(Exception)
        {
            a = true;
        }
    }

    if( a )
    {
        return x;
    }
}

Непосредственное исправление заключается в том, чтобы просто удалить оператор if( a ) guard полностью и просто return x немедленно, но почему компилятор жалуется, хотя он должен иметь возможность статически доказывать, что все возможные пути кода попадут в оператор return? Существенно, что нет петель, которые часто являются основной причиной того, что он не докажет return -ness.

Я использую VS2015 Update 3.

4b9b3361

Ответ 1

Поддерживается сценарий a false, когда вы достигаете конца своей функции. Этот сценарий - это когда вы отлаживаете свой код и используете отладчик для установки a на false.

Правила компилятора С# по дизайну просты. На языках, которые С# занимает, проблема функций, потенциально не возвращающих ничего, была проблемой, которая не могла быть охвачена предупреждениями компилятора. Было слишком много ложных срабатываний, поэтому предупреждения были изменены, чтобы предупредить только об очевидных случаях, введя ложные негативы. Правила С# являются компромиссом, когда ложные срабатывания приемлемы, если они понятны человеку, знакомому с правилами, а ложные негативы неприемлемы. Вам гарантировано, что если ваша функция имеет путь к коду, который не возвращает значение, компилятор обнаруживает его.

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

@PetSerAl уже цитирует соответствующую формулировку в спецификации языка С#:

8.1 Конечные точки и достижимость

[...]

Чтобы определить, доступен ли конкретный оператор или конечная точка, компилятор выполняет анализ потока в соответствии с правилами достижимости, определенными для каждого оператора. Анализ потока учитывает значения константных выражений (§7.19), которые управляют поведением операторов, но возможные значения непостоянных выражений не рассматриваются. Другими словами, для целей анализа потока управления считается, что непостоянное выражение данного типа имеет любое возможное значение этого типа.

Это часть последней опубликованной спецификации языка, для С# 5.0. Версия, которую вы используете, С# 6.0 (то, что предлагает VS2015), пока не имеет опубликованной спецификации, поэтому возможно, что формулировка будет немного отличаться, но, как показал ваш компилятор, фактически применяется то же правило.

Ответ 2

Это время выполнения и время компиляции

Ваш пример слишком сложный. Это также не будет компилироваться:

static int Test()
{
    bool f = true;
    if (f)
    {
        return 1;
    }
    else
    {
        //Not all code paths return a value
    }
}

С другой стороны, это будет:

static int Test()
{
    if (true)
    {
        return 1;
    }
    else
    {
        //No error
    }
}

Я предполагаю, что все механизмы проверки не имеют достаточной логики для вывода содержимого переменной времени выполнения. Компиляционные переменные не являются проблемой.

Ответ 3

Я думаю, что компилятор делает очень простой анализ кода, и, таким образом, нужно явно указать return.

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

Ваш пример можно свести к минимуму следующим образом:

public static Int32 Main(String[] args)
{
    var printUsage = true;
    if (printUsage)
    {
        return 0;
    }

    // return nothing, so compiler is not happy
}

все еще получая ошибку.

ПРИМЕЧАНИЕ:, если вы используете Resharper, он выполнит необходимый анализ и предупредит вас:

if (printUsage)         // Warning: expression is always true