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


107

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

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

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

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

왜?


7
그래서. 그냥 궁금해. 왜 컴파일되지 않는지 완전히 이해가된다면 왜 누군가가 이미 말이되는 것을 설명해주기를 원합니까? =)
J. Steen

14
당신은 왜이 필요 continue;finally블록? 블록 continue;이후 와 같지 try -- catch않나요?
bansi

6
@ J.Steen 아니요, 이것이 finally/continueC # 컴파일러의 제한 이라는 것을 알고 있습니다. :-)이 제한의 이유에 대해서도 궁금합니다.
xanatos

5
@xanatos-기술적으로 말하면 기본 CIL의 제한 사항입니다. 로부터 언어 사양 : "제어 전송은 예외 처리 메커니즘을 통해 제외 catch 핸들러 또는 마지막 절을 입력하는 것이 허용되지 않습니다." 및 "보호 된 영역에서 제어 전송은 예외 명령 (leave, end.filter, end.catch 또는 end.finally)을 통해서만 허용됩니다." br지부 지시 의 가족은 이것을 달성 할 수 없습니다.
Unsigned

1
@Unsigned : 그것도 좋은 제한입니다, 그것은 기괴 할 것입니다 :)
user7116

답변:


150

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

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

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

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

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

예외를 무시하고 (나쁜 생각이 아닌 경우가 많음) 루프를 계속 실행하려면 catch all 블록을 사용하십시오.

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

continue포착되지 않은 예외가 발생하지 않을 때만 원하는 경우 continuetry-block 외부에 두십시오 .


12
Microsoft가 마지막으로 계속을 수락하지 않기로 결정한 이유를 이해했기 때문에 나는 이것을 대답으로 받아 들일 것입니다. 아마 이미지 : 나도 확신 것을
lpaloub

20
당신은 "성공의 구덩이에 당신을 던져"아이디어를 자세히 설명 할 수 있습니까? 나는 D는하지 않았다
개미


13
이미지는 Jon Skeet, btw에 의해 만들어졌습니다. 여기에서 가져온 : msmvps.com/blogs/jon_skeet/archive/2010/09/02/...
R. 마르틴 페르난데스

1
물론, continueA의 finally그것만 내에 정의 된 로컬 루프 계속되면 블록 OK 것이다 finally블록. 그러나 질문에서는 "외부"루프를 계속하려고합니다. finally블록 내부에서 가질 수없는 유사한 문장 은 return, break( 블록 을 벗어날 때) 및 goto( finally블록 외부의 레이블로 이동할 때 )입니다. 관련 Java 토론 은 Java의 finally 블록에서 반환을 참조하십시오 .
Jeppe Stig Nielsen

32

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

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 문 .


31

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

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

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

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

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


관련 메모에서 "catch"가 없더라도 finally문 뒤의 실행 경로 는 catch이 예외를 통해 종료 되었는지 여부에 따라 영향을받을 수 있으며 continue. 그러나 더 큰 문제를 보여주기 때문에 귀하의 예가 좋습니다.
supercat 2013-08-02

@supercat 동의합니다. 제 대답은 컴파일러에 대한 모호한 상황의 예를 보여줍니다.이 접근법에는 여전히 많은 문제가 있습니다.
스리 람 Sakthivel

16

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

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

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;
}

이론적으로는 제어 흐름을 추적하고 "좋아"라고 말할 수 있습니다. 예외가 발생하지 않으며 제어가 전송되지 않습니다. 그러나 C # 언어 디자이너는 다른 문제를 염두에 두었습니다.

던진 예외

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

공포의 고토

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

    pigsfly:
}

반환

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

헤어짐

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

이 동안 그래서 결론적으로, 그래, 이다 약간의 사용 a의 가능성 continue제어가 전송되지 않는 상황에서,하지만 좋은 거래 (대부분?)의 경우는 예외 또는 포함 return블록. 언어 디자이너는 이것이 너무 모호하고 컴파일 타임 에 제어 흐름이 전송되지 않는 경우 에만continue 사용 된다는 것을 보장하기가 불가능하다고 느꼈습니다 .


난독 화 중에 proguard가 충돌했을 때 Java에서 동일한 상황이 발생했습니다. 당신의 설명은 꽤 좋습니다. C #은 컴파일 시간 오류를 제공합니다.
Dev_Vikram

11

일반적으로 블록 continue에서 사용할 때 의미가 없습니다 finally. 이것 좀보세요 :

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

"일반적으로"많은 것들이 말이되지 않습니다. 그리고 그것이 "특히"작동해서는 안된다는 의미도 아닙니다. IE : continue외부 루프 문은 의미가 없지만 지원되지 않는다는 의미는 아닙니다.
zerkms

말이되는 것 같아요. item파일 일 수 있으며 읽기 실패-> finally파일을 닫습니다. continue나머지 처리를 방지합니다.
jnovacho

5

"이것은 컴파일되지 않을 것이며 완전히 말이되는 것 같습니다."

글쎄, 그렇지 않다고 생각합니다.

말 그대로 있으면 catch(Exception)finally (그리고 아마도)가 필요하지 않습니다 continue.

더 현실적 catch(SomeException)일 때 예외가 포착되지 않으면 어떻게해야합니까? 당신 continue은 한 방향으로 가고 싶어하고, 예외는 다른 것을 처리합니다.


2
나는 finally당신이 가질 때 필요할 수 있다고 생각합니다 catch. 일반적으로 신뢰할 수있는 방식으로 리소스를 닫는 데 사용됩니다.
jnovacho

하지만 예외가 아닙니다. 당신은 할 수 쉽게return당신의 내 문 try또는 catch블록을. 이제 우리는 무엇을합니까? 루프를 반환하거나 계속 하시겠습니까? (? 우리는 계속 할 경우, 그것은 그 다음 우리가 외부에서 계속 반복되는 마지막 항목이 무엇인지 foreachEDIT? 및 반환은 결코 일어나지 않습니다) : 심지어 goto루프 외부의 라벨에, 거의 모든 작업이 전송은 외부 제어하는 foreach루프 .
Chris Sinclair

2
나는 "말 그대로 catch (Exception)가있을 때 finally가 필요하지 않습니다 (아마도 계속할 필요도 없습니다)."에 동의하지 않습니다. 예외가 발생했는지 여부에 관계없이 작업을 수행해야하는 경우 어떻게합니까?
lpaloub

좋아, 마지막으로 return블록 내부 에 추가 s를 제공합니다. 그러나 일반적으로 실행은 모두 잡은 후에도 계속됩니다.
Henk Holterman 2013-08-01

'finally'는 'catch'가 아닙니다. 정리 코드에 사용해야합니다. finally 블록 은 예외 발생 여부에 관계없이 실행됩니다 . 파일 닫기, 메모리 해제 (관리되지 않는 코드를 사용하는 경우) 등과 같은 항목이 finally 블록에 넣는 항목입니다.
Robotnik

3

finally 블록의 본문을 떠날 수 없습니다. 여기에는 중단, 반환 및 귀하의 경우 계속 키워드가 포함됩니다.


3

finally블록 예외 재 throw 기다리고으로 실행될 수있다. continue예외를 다시 던지지 않고 (또는 다른 방법으로) 블록을 종료 할 수 있다는 것은 실제로 이치에 맞지 않습니다 .

무슨 일이 있어도 루프를 계속하려면 finally 문이 필요하지 않습니다. 예외를 포착하고 다시 던지지 마십시오.


1

finally포착되지 않은 예외가 발생하는지 여부에 관계없이 실행됩니다. 다른 사람들은 이것이 왜 continue비논리적으로 만드는지 이미 설명 했지만, 여기에이 코드가 요구하는 것처럼 보이는 정신을 따르는 대안이 있습니다. 기본적으로 다음과 finally { continue; }같이 말합니다.

  1. 예외가 발견되면 계속
  2. 포착되지 않은 예외가있는 경우 예외가 발생하도록 허용하지만 계속 진행합니다.

(1) continue각 끝에 배치 하여 만족시킬 수 있으며 catch(2) 나중에 던질 잡히지 않은 예외를 저장하여 만족시킬 수 있습니다. 다음과 같이 작성할 수 있습니다.

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);
}

사실, finally잡혔거나 잡히지 않은 예외가 전혀 발생하지 않은 세 번째 경우에서도 실행되었을 것입니다. 원하는 경우, 물론 continue각 내부 대신 try-catch 블록 뒤에 하나를 배치 할 수 catch있습니다.


1

기술적으로 말하면 기본 CIL의 제한 사항입니다. 로부터 언어 사양 :

제어 전송은 예외 처리 메커니즘을 통하지 않는 한 catch 핸들러 또는 finally 절에 들어갈 수 없습니다.

보호 영역 밖으로 제어 전송에만 예외 명령을 허용 ( leave, end.filter, end.catch, 또는 end.finally)

br지침에 대한 문서 페이지에서 :

try, catch, filter 및 finally 블록에 대한 제어 전송은이 명령으로 수행 할 수 없습니다.

이 마지막에 대한 사실이 보유하고 있는 모든 분기 명령을 포함 beq, brfalse


-1

언어의 설계자는 제어 전송에 의해 종료되는 finally 블록의 의미에 대해 추론하고 싶지 않았거나 할 수 없었습니다.

한 가지 문제 또는 아마도 핵심 문제는 finally블록이 일부 비 로컬 제어 전송 (예외 처리)의 일부로 실행 된다는 것 입니다. 제어 전송의 대상은 둘러싸는 루프가 아닙니다. 예외 처리는 루프를 중단하고 계속해서 풀립니다.

우리가 밖으로 제어 전송하는 경우 finally정리 블록을, 다음 원래 컨트롤 전송은 "납치"되고있다. 취소되고 제어가 다른 곳으로 이동합니다.

의미론을 해결할 수 있습니다. 다른 언어에도 있습니다.

C #의 설계자는 단순히 정적이고 "고토와 같은"컨트롤 전송을 허용하지 않기로 결정하여 작업을 다소 단순화했습니다.

그러나 그렇게하더라도 동적 전송이 a에서 시작되면 어떻게되는지에 대한 질문을 해결하지 못합니다 finally. finally 블록이 함수를 호출하고 해당 함수가 throw되면 어떻게됩니까? 그러면 원래 예외 처리가 "도용"됩니다.

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


차이점은 finally정의에 따라 즉시이를 트리거 한 전송을 계속 해야 한다는 것입니다 (포착되지 않은 예외 return등). 내에서 "goto-like"전송을 허용하는 것은 쉬울 것입니다 finally(어느 언어가이 작업을 수행합니까?). 그렇게하면 완전히 예상치 못한를 try { return } finally { ... } 반환하지 않을 수 있습니다 . 만약 finally호출 우리가 이미 예외가 언제라도 발생할 정상적인 흐름을 방해 할 수 있다는 기대 때문에, 정말 같은 일이 아니다 발생하는 기능.
nmclean

@nmclean : 실제로 이스케이프하는 예외 finally는 예상치 못한 비논리적 인 프로그램 흐름을 초래할 수 있다는 점에서 동일한 것입니다. 유일한 차이점은 언어 설계자는 finally 블록이 처리되지 않은 예외를 throw 할 수있는 모든 프로그램을 거부 할 수 없다는 것입니다. 대신 이러한 프로그램이 컴파일되도록 허용하고 프로그램이 이전 예외 해제를 포기한 후 발생할 수있는 결과를 받아 들일 수 있기를 바랍니다. 순서.
supercat '2013-08-02

@supercat 나는 그것이 예상되는 흐름을 깨는 것에 동의하지만, 내 요점은 현재 흐름이 있는지 여부에 관계없이 항상 처리되지 않은 예외의 경우 라는 것입니다 finally. Kaz의 마지막 단락의 논리에 따라 함수는 continue예외를 통해 그렇게 할 수 있기 때문에 호출자 범위의 흐름에 자유롭게 영향을 줄 수 있어야합니다 . 그러나 이것은 물론 허용되지 않습니다. 예외는 이 규칙 의 유일한 예외이므로 이름은 "예외"(또는 "인터럽트")입니다.
nmclean

@nmclean : 비 중첩 예외는 일반 실행과는 다르지만 여전히 구조화 된 제어 흐름을 나타냅니다. 블록 뒤의 문은 해당 블록 내에서 발생한 모든 예외가 해당 블록 내에서 포착 된 경우에만 실행되어야합니다. 이는 합리적인 기대처럼 보일 수 있지만 finally블록 내에서 발생하는 예외 는이를 위반할 수 있습니다. IMHO는 최소한 finally보류중인 예외에 대해 블록에 알려서 복합 예외 개체를 빌드 할 수 있도록 하여 해결해야하는 정말 끔찍한 상황 입니다.
supercat 2013-08-02

@mmclean 죄송합니다. "예외"라는 이름이 C #, LOL에 특정한 "규칙에 대한 예외"(제어 관련)에서 비롯된다는 데 동의하지 않습니다. 예외의 측면을 변경하는 제어 흐름은 단순히 로컬이 아닌 동적 제어 전송입니다. 동일한 어휘 범위 내에서 정기적 인 제어 전송 (해제되지 않음)은 특별한 경우로 간주 될 수 있습니다.
Kaz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.