program tip

'continue'문이 'finally'블록 안에있을 수없는 이유는 무엇입니까?

radiobox 2020. 8. 11. 08:14
반응형

'continue'문이 'finally'블록 안에있을 수없는 이유는 무엇입니까?


나는 문제가 없습니다. 그냥 궁금 해요. 다음 시나리오를 상상해보십시오.

foreach (var foo in list)
{
    try
    {
         //Some code
    }
    catch (Exception)
    {
        //Some more code
    }
    finally
    {
        continue;
    }
}

컴파일러 오류 CS0157이 발생하므로 컴파일되지 않습니다 .

제어는 finally 절의 본문을 벗어날 수 없습니다.

왜?


finally블록은 예외 발생 여부에 관계없이 실행됩니다. 예외가 발생하면 도대체 continue어떻게할까요? 잡히지 않은 예외가 다른 함수로 제어를 전송하기 때문에 루프 실행을 계속할 수 없습니다.

예외가 발생하지 않더라도 finally는 try / catch 블록 내의 다른 제어 전송 문이 실행될 때 실행됩니다 ( return예 : 같은 문제가 발생하는).

요컨대, finally그것 의 의미론으로 인해 finally블록 내부 에서 외부 로 제어를 전송하는 것은 의미 가 없습니다 .

의도 된 동작을 더 명확하게 만드는 간단한 해결 방법이 있기 때문에 일부 대체 의미 체계로이를 지원하는 것은 도움이되는 것보다 더 혼란 스러울 것입니다. 따라서 오류가 발생하고 문제에 대해 제대로 생각해야합니다. C #에서 계속되는 것은 일반적인 "성공의 구덩이에 던져 넣는"아이디어입니다.

C #, 당신, 그리고 성공한다면

예외를 무시하고 (나쁜 생각이 아닙니다) 루프를 계속 실행하려면 catch all 블록을 사용하십시오.

foreach ( var in list )
{
    try{
        //some code
    }catch{
        continue;
    }
}

continue잡히지 않은 예외가 발생하지 않을 때만 원한다면 continuetry-block 밖에 두십시오 .


다음은 신뢰할 수있는 출처입니다.

continue 문은 finally 블록을 종료 할 수 없습니다 (섹션 8.10). finally 블록 내에서 continue 문이 발생하면 continue 문의 대상은 동일한 finally 블록 내에 있어야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

MSDN, 8.9.2 에서 가져온 것입니다 . continue 문 .

문서는 다음과 같이 말합니다.

finally 블록의 문은 제어가 try 문을 떠날 때 항상 실행됩니다. 제어 전송이 정상 실행의 결과로 발생하는지, break, continue, goto 또는 return 문 실행의 결과로 발생하든, 또는 try 문에서 예외를 전파 한 결과로 발생하든 마찬가지입니다. finally 블록을 실행하는 동안 예외가 발생하면 예외는 다음 엔 클로징 try 문으로 전파됩니다. 전파중인 다른 예외가 있으면 해당 예외가 손실됩니다. 예외를 전파하는 프로세스는 throw 문에 대한 설명에서 자세히 설명합니다 (섹션 8.9.5).

여기에서 8.10 try 문 .


말이된다고 생각할 수도 있지만 실제로 는 말이되지 않습니다 .

foreach (var v in List)
{
    try
    {
        //Some code
    }
    catch (Exception)
    {
        //Some more code
        break; or return;
    }
    finally
    {
        continue;
    }
}

예외가 발생할 때 중단 또는 계속 을 수행 할 의도는 무엇입니까 ? C # 컴파일러 팀은 break또는 continue. 대신, 그들은 개발자 상황이 .NET에서 제어권을 이전하는 것이 모호하다고 불평하기로 결정했습니다 finally block.

따라서 컴파일러가 다른 것을 가정하는 것보다 자신이하려는 일을 명확하게 밝히는 것이 개발자의 임무입니다.

이것이 컴파일되지 않는 이유를 이해하기를 바랍니다!


다른 사람들이 말했듯이 예외에 초점을 맞추는 것은 실제로 제어 전송의 모호한 처리에 관한 것입니다.

당신은 아마도 다음과 같은 시나리오를 생각하고있을 것입니다.

public static object SafeMethod()
{
    foreach(var item in list)
    {
        try
        {
            try
            {
                //do something that won't transfer control outside
            }
            catch
            {
                //catch everything to not throw exceptions
            }
        }
        finally
        {
            if (someCondition)
                //no exception will be thrown, 
                //so theoretically this could work
                continue;
        }
    }

    return someValue;
}

Theoretically, you can track the control flow and say, yes, this is "ok". No exception is thrown, no control is transferred. But the C# language designers had other issues in mind.

The Thrown Exception

public static void Exception()
{
    try
    {
        foreach(var item in list)
        {
            try
            {
                throw new Exception("What now?");
            }
            finally
            {
                continue;
            }
        }
    }
    catch
    {
        //do I get hit?
    }
}

The Dreaded Goto

public static void Goto()
{
    foreach(var item in list)
    {
        try
        {
            goto pigsfly;
        }
        finally
        {
            continue;
        }
    }

    pigsfly:
}

The Return

public static object ReturnSomething()
{
    foreach(var item in list)
    {
        try
        {
            return item;
        }
        finally
        {
            continue;
        }
    }
}

The Breakup

public static void Break()
{
    foreach(var item in list)
    {
        try
        {
            break;
        }
        finally
        {
            continue;
        }
    }
}

So in conclusion, yes, while there is a slight possibility of using a continue in situations where control isn't being transferred, but a good deal (majority?) of cases involve exceptions or return blocks. The language designers felt this would be too ambiguous and (likely) impossible to ensure at compile time that your continue is used only in cases where control flow is not being transferred.


In general continue does not make sense when used in finally block. Take a look at this:

foreach (var item in list)
{
    try
    {
        throw new Exception();
    }
    finally{
        //doesn't make sense as we are after exception
        continue;
    }
}

"This won't compile and I think it makes complete sense"

Well, I think it doesn't.

When you literally have catch(Exception) then you don't need the finally (and probably not even the continue).

When you have the more realistic catch(SomeException), what should happen when an exception is not caught? Your continue wants to go one way, the exception handling another.


You cannot leave the body of a finally block. This includes break, return and in your case continue keywords.


The finally block can be executed with an exception waiting to be rethrown. It wouldn't really make sense to be able to exit the block (by a continue or anything else) without rethrowing the exception.

If you want to continue your loop whatever happens, you do not need the finally statement: Just catch the exception and don't rethrow.


finally runs whether or not an uncaught exception is thrown. Others have already explained why this makes continue illogical, but here is an alternative that follows the spirit of what this code appears to be asking for. Basically, finally { continue; } is saying:

  1. When there are caught exceptions, continue
  2. When there are uncaught exceptions, allow them to be thrown, but still continue

(1) could be satisfied by placing continue at the end of each catch, and (2) could be satisfied by storing uncaught exceptions to be thrown later. You could write it like this:

var exceptions = new List<Exception>();
foreach (var foo in list) {
    try {
        // some code
    } catch (InvalidOperationException ex) {
        // handle specific exception
        continue;
    } catch (Exception ex) {
        exceptions.Add(ex);
        continue;
    }
    // some more code
}
if (exceptions.Any()) {
    throw new AggregateException(exceptions);
}

Actually, finally would have also executed in the third case, where there were no exceptions thrown at all, caught or uncaught. If that was desired, you could of course just place a single continue after the try-catch block instead of inside each catch.


Technically speaking, it's a limitation of the underlying CIL. From the language spec:

Control transfer is never permitted to enter a catch handler or finally clause except through the exception handling mechanism.

and

Control transfer out of a protected region is only permitted through an exception instruction (leave, end.filter, end.catch, or end.finally)

On the doc page for the br instruction:

Control transfers into and out of try, catch, filter, and finally blocks cannot be performed by this instruction.

This last holds true for all branch instructions, including beq, brfalse, etc.


The designers of the language simply didn't want to (or couldn't) reason about the semantics of a finally block being terminated by a control transfer.

One issue, or perhaps the key issue, is that the finally block gets executed as part of some non-local control transfer (exception processing). The target of that control transfer isn't the enclosing loop; the exception processing aborts the loop and continues unwinding further.

If we have a control transfer out of the finally cleanup block, then the original control transfer is being "hijacked". It gets canceled, and control goes elsewhere.

The semantics can be worked out. Other languages have it.

The designers of C# decided to simply disallow static, "goto-like" control transfers, thereby simplifying things somewhat.

However, even if you do that, it doesn't solve the question of what happens if a dynamic transfer is initiated from a finally: what if the finally block calls a function, and that function throws? The original exception processing is then "hijacked".

이 두 번째 형태의 하이재킹의 의미를 해결하면 첫 번째 유형을 추방 할 이유가 없습니다. 그것들은 실제로 똑같습니다. 제어 전송은 그것이 동일한 어휘 범위이든 아니든간에 제어 전송입니다.

참고 URL : https://stackoverflow.com/questions/17991036/why-cant-a-continue-statement-be-inside-a-finally-block

반응형