2017-01-13 12 views
3

я имел функцию, возвращавшуюся в using блоке:C# компилятор не жалуется, когда не все пути кода возврата

int f() { 
    using (...) { 
    ... 
    return someVar; 
    } 
} 

Я только заметил это и перевёл return за пределы using блока к внешнему потому что я чувствовал, что именно там должно быть return.

Но я смущен, почему компилятор не жаловался, что не все коды кода возвращались. Это просто потому, что если ему не удалось инициализировать ресурс, мы получим крах, так что это не имеет значения?

Возьмем такой пример:

class MainClass { 
    public static void Main (string[] args) { 
    f(); 
    } 

    public static int f() { 
    using(A a = new A()) { 
     return 1; 
    } 
    } 
} 

class A : IDisposable{ 
    public void Dispose() { } 
} 

Компилятор не волнует, что мы только вернуться в using. Тем не менее, я думал, что using заявления были в основном синтаксический сахар для try/catch

Если заменить

using(A a = new A()) { 
    return 1; 
} 

с

A a = new A();; 
try { 
    return 1; 
} 

catch (Exception e) { } 
finally { 
    if (a != null) { 
    ((IDisposable) a).Dispose(); 
    } 
} 

Действительно, компилятор жалуется: CS0161

ошибка: `MainClass .f() ': не все пути кода возвращают значение

Почему он не жалуется в другом случае?

Это просто то, что я сказал выше? Если он не может инициализировать ресурс, мы получим крах, поэтому компилятор решит, что это не имеет значения.

+7

Это просто «try-finally», нет «catch». Если вы удалите 'catch' в своем коде, вы не получите ошибку компиляции. – juharr

+0

Почему вы хотите вытащить 'return' из' use'? Это делает код намного сложнее и труднее читать, не добавляя ничего взамен. – Servy

+0

@Servy Я не понимаю, как это делает его «намного сложнее». Причина в том, что я хочу вернуться в самый отдаленный блок - это как-то кажется более правильным. – pushkin

ответ

5

На самом деле:

using(var objectName = <init-expression>) { 
    //... 
} 

более или менее эквивалентно:

objectName = <init-expression>; 
try { 
    //... 
} finally { 
    objectName.Dispose(); 
} 

Так это try - finally -блок: если что-то пойдет не так во время выполнения, исключение будет выбрасывается из метода (в основном после завершения finally).

try - finally однако не создает альтернативный путь коды: если try -части возвращает что-то или выдает ошибку, он сначала выполнить finally -части, но затем либо выбросить исключение или вернуть то, что должно возвращаться.

4

По существу, да. Оператор using выбросит исключение из стека. Улавливание исключения и использование блока finally, который не возвращает, означает, что метод не генерирует исключение и не возвращается.

Компилятор также не жалуется на методы, такие как следующий

public int SuperCool(){ 
    throw new NotImplementedException("bummer"); 
} 

Чтобы немного расширить, так как я пропустил правку с блока поймать не там изначально:

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