Try 블록에 값을 반환하면 Final 문의 코드가 실행됩니까?


237

친구를위한 코드를 검토하고 있으며 try-finally 블록 내부에서 return 문을 사용하고 있다고 말합니다. try 블록의 나머지 부분이 작동하지 않더라도 Final 섹션의 코드가 여전히 실행됩니까?

예:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}


여기서 처리된다는 것은 잡히는 것을 의미합니다. 글로벌 핸들러의 빈 캐치 블록으로도 충분합니다. 또한 처리 할 수없는 예외가 있습니다 : StackOverflowException, 그 ExecutionEngineException중 일부 는 예외입니다 . 그리고 그들은 처리 할 수 ​​없기 때문에 finally실행되지 않습니다.
Abel

8
@ 아벨 : 당신은 다른 상황에 대해 이야기하는 것 같습니다. 이 질문에 관한 반환 A의 try블록. 갑작스러운 프로그램에 대해서는 아무것도 중단되지 않습니다.
Jon Skeet

6
@Abel : "예외 후 반환"이 무슨 뜻인지 잘 모르겠지만, 여기에서 묻는 것이 아닌 것 같습니다. 코드를보십시오- try블록 의 첫 번째 문장은 return문장입니다. (그 블록의 두 번째 문 도달하고 경고를 발생한다.)
존 소총

4
@Abel : 사실, 질문이 "마지막 진술의 코드는 항상 모든 상황에서 실행될 것"이라면 관련이있을 것입니다. 그러나 그것은 요구되는 것이 아닙니다.
Jon Skeet

답변:


265

간단한 대답 : 그렇습니다.


9
@ 에디 : 음, 나는 배경 스레드가 그것과 어떤 관련이 있는지 알지 못합니다. 기본적으로 전체 프로세스가 중단되지 않으면 finally블록이 실행됩니다.
Jon Skeet

3
긴 대답은 스택 오버플로, 메모리 부족 예외, 어떤 종류의 하드 크래시 또는 누군가가 적시에 컴퓨터의 플러그를 뽑은 경우와 같이 치명적인 문제가 발생한 경우 finally 블록을 실행하지 않아도된다는 것입니다. . 그러나 모든 의도와 목적을 위해, 매우 잘못된 일을하지 않으면 finally 블록이 항상 실행됩니다.
Andrew Rollings

어떤 이유로 시도하기 전에 코드가 실패하면 마침내 여전히 실행된다는 것을 알았습니다. 이 경우 마지막에 코드가 성공적으로 실행될 때만 최종적으로 실행되는 기준을 만족시키는 조건을 추가 할 수 있습니다.
kjosh

200

일반적으로 그렇습니다. finally 섹션은 예외 또는 return 문을 포함하여 발생하는 모든 것을 실행하도록 보장됩니다. 이 규칙에 대한 예외는 스레드 ( OutOfMemoryException, StackOverflowException) 에서 발생하는 비동기 예외 입니다.

해당 상황에서 비동기 예외 및 안정적인 코드에 대해 자세히 알려면 제한된 실행 영역 에 대해 읽으십시오 .


154

다음은 약간의 테스트입니다.

class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("before");
        Console.WriteLine(test());
        Console.WriteLine("after");
    }

    static string test()
    {
        try
        {
            return "return";
        }
        finally
        {
            Console.WriteLine("finally");
        }
    }
}

결과는 다음과 같습니다.

before
finally
return
after

10
@CodeBlend : WriteLine메소드 호출 결과가 실제로 수행 될 때와 관련이 있습니다. 이 경우 return호출은 메소드 결과를 설정하고 마지막으로 메소드를 종료하기 전에 콘솔에 씁니다. 그런 다음 WriteLineMain 메소드에서 리턴 호출에서 텍스트를 뱉어냅니다.
NotMe

38

MSDN에서 인용

마지막으로 앞의 try 블록이 어떻게 종료 되는지에 관계없이 명령문 코드 블록 실행을 보장하는 데 사용됩니다 .


3
글쎄, Mehrdad의 예외적 인 경우 외에도 try 블록에서 디버깅 한 다음 디버깅을 중지하면 마지막으로 호출되지 않습니다. :). 인생에서 아무것도 보장되지 않습니다.
StuartLC

1
MSDN 에서 인용 것은 아니지만 : 예외가 처리되지 않으면 finally 블록의 실행은 예외 해제 작업이 어떻게 트리거되는지에 달려 있습니다. 이는 컴퓨터 설정 방법에 따라 다릅니다. .
Abel

1
@Abel은 여기서 중요한 점을 지적합니다. 예를 들어 프로그램이 작업 관리자를 통해 중단되면 모든 사람이 finally 블록이 실행되지 않는 방법을 볼 수 있습니다. 그러나이 대답은 명백한 잘못입니다. finally사실 완벽하게 일반적인 프로그램 (이 예외를 던진)조차도 아무것도 보장 하지 않습니다.
피터-모니카 복직 자

19

일반적으로 그렇습니다. 마지막으로 실행됩니다.

다음 세 가지 시나리오의 경우 최종적으로 항상 실행됩니다.

  1. 예외는 발생하지 않습니다
  2. 동기 예외 (정상 프로그램 흐름에서 발생하는 예외).
    여기에는 System.Exception에서 파생 된 CLS 호환 예외와 System.Exception에서 파생되지 않은 CLS 비준수 예외가 포함됩니다. CLS 비준수 예외는 RuntimeWrappedException에 의해 자동으로 줄 바꿈됩니다. C #은 CLS 이외의 불만 예외를 처리 할 수 ​​없지만 C ++와 같은 언어는 예외를 처리 할 수 ​​있습니다. C #은 비 CLS 규격 예외를 발생시킬 수있는 언어로 작성된 코드를 호출 할 수 있습니다.
  3. 비동기 ThreadAbortException
    .NET 2.0부터 ThreadAbortException은 더 이상 최종 실행을 방해하지 않습니다. ThreadAbortException은 이제 마지막 이전 또는 이후에 게양됩니다. 스레드 중단이 발생하기 전에 try가 실제로 입력 된 경우 마지막은 항상 실행되며 스레드 중단에 의해 중단되지 않습니다.

다음 시나리오에서는 최종적으로 실행되지 않습니다.

비동기 StackOverflowException.
.NET 2.0부터 스택 오버플로로 인해 프로세스가 종료됩니다. 마지막으로 CER (Constrained Execution Region)을 만들기 위해 추가 제한 조건이 적용되지 않는 한 마지막은 실행되지 않습니다. CER은 일반 사용자 코드에서 사용해서는 안됩니다. 모든 프로세스가 스택 오버플로에서 종료 된 후 정리 코드가 항상 실행되어야하는 중요한 위치에서만 사용해야하며, 따라서 모든 관리 대상 객체는 기본적으로 정리됩니다. 따라서 CER과 관련이있는 유일한 위치는 프로세스 외부에 할당 된 리소스 (예 : 관리되지 않는 핸들)입니다.

일반적으로 비 관리 코드는 사용자 코드가 사용하기 전에 일부 관리 클래스에 의해 랩핑됩니다. 관리 랩퍼 클래스는 일반적으로 SafeHandle을 사용하여 관리되지 않는 핸들을 랩합니다. SafeHandle은 중요한 종료 자와 CER에서 실행되는 정리 메소드를 구현하여 정리 코드의 실행을 보장합니다. 이러한 이유로 CER이 사용자 코드를 통해 흩어지지 않아야합니다.

따라서 최종적으로 StackOverflowException에서 실행되지 않는다는 사실은 프로세스가 종료되기 때문에 사용자 코드에 영향을 미치지 않습니다. SafeHandle 또는 CriticalFinalizerObject 외부에서 관리되지 않는 리소스를 정리해야하는 경우가있는 경우 다음과 같이 CER을 사용하십시오. 관리되지 않는 개념은 설계 상 관리되는 클래스 및 적절한 SafeHandle로 추상화되어야합니다.

예를 들어

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{ 
    // This is *NOT* a CER
}
finally
{
    // This is a CER; guaranteed to run, if the try was entered, 
    // even if a StackOverflowException occurs.
}

SOE의 경우 CER도 실행되지 않습니다. 작성 당시 CER에 대한 MSDN 설명서가 잘못되었거나 불완전합니다. 공기업은 FailFast내부적으로 트리거합니다 . 내가 그것을 잡을 수있는 유일한 방법 은 CLR 런타임 호스팅사용자 정의하는 것 입니다. 요점은 여전히 ​​다른 비동기 예외에 유효합니다.
Abel

10

나는 다른 답변에서 언급하지 않았고 (C #에서 18 년 동안 프로그래밍 한 후) 알지 못했던 믿을 수없는 매우 중요한 예외가 있습니다.

블록 내부 에서 모든 종류 의 예외를 던지거나 트리거하면 catch(이상 StackOverflowExceptions하고 그 일이 아닌 것들) try/catch/finally다른 try/catch블록 안에 전체 finally블록이 없으면 블록이 실행되지 않습니다. 이것은 쉽게 시연됩니다-그리고 내가 그것을 직접 보지 못했다면, 얼마나 자주 finally블록을 실행하지 못하게 할 수있는 정말 이상하고 작은 코너 케이스라는 것을 읽었을 때 , 나는 그것을 믿지 않았을 것입니다.

static void Main(string[] args)
{
    Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
    try
    {
        Console.WriteLine("Inside try but before exception.");
        throw new Exception("Exception #1");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
        throw;
    }
    finally
    {
        Console.WriteLine("This never gets executed, and that seems very, very wrong.");
    }

    Console.WriteLine("This never gets executed, but I wasn't expecting it to."); 
    Console.ReadLine();
}

나는 이것에 대한 이유가 있다고 확신하지만 더 널리 알려지지 않은 것은 기괴합니다. ( 예를 들어 여기 에 언급되어 있지만이 특정 질문의 어느 곳에서도 언급 되지 않았습니다.)


글쎄, finally블록은 단순히 블록을 잡는 것이 아닙니다 catch.
피터-복원 모니카

7

나는 파티에 늦었지만 실제로 MSDN 상태 ( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ) 에서 예외가 발생하는 시나리오 (OP의 예와 다름)에 있음을 알고 있습니다 . "예외가 발견되지 않으면 finally 블록의 실행은 운영 체제가 예외 해제 작업을 트리거할지 여부에 따라 결정 됩니다."

finally 블록은 Main과 같은 다른 함수가 콜 스택을 넘어서서 예외를 잡을 경우 에만 실행되도록 보장 됩니다. 모든 런타임 환경 (CLR 및 OS) C # 프로그램은 프로세스가 종료 할 때 소유하고있는 대부분의 리소스 (파일 핸들 등)에서 무료로 실행되므로이 ​​세부 사항은 일반적으로 문제가되지 않습니다. 일부 경우에는 중요 할 수 있습니다. 데이터베이스 조작이 절반 정도 진행되어 resp를 커밋하려고합니다. 풀다; 또는 OS에 의해 자동으로 닫히지 않고 서버를 차단할 수있는 일부 원격 연결.


3

예. 그것이 사실 최종 진술의 요점입니다. 치명적인 일이 발생하지 않는 한 (메모리 부족, 컴퓨터 분리 등) finally 문은 항상 실행해야합니다.


1
동의하지 않습니다. Cf. 내 대답. try 블록에서 완벽하게 정상적인 예외는 finally예외가 발견되지 않으면 블록 을 무시하기에 충분합니다 . 그것은 놀랍게도 나를 붙 잡았다 ;-).
피터-모니카 복직 자

@ PeterA.Schneider-흥미 롭습니다. 물론 그 의미는 크게 변하지 않습니다. 호출 스택에서 예외를 잡을 수있는 것이 없다면 프로세스가 종료 될 것임을 올바르게 이해해야합니다. 따라서 플러그를 뽑는 상황과 비슷합니다. 이것으로부터의 테이크 아웃은 항상 최상위 또는 처리되지 않은 예외를 잡아야한다는 것입니다.
Jeffrey L Whitledge

정확히, 그것은 내가 이해하는 것입니다. 나도 그 놀라운 것을 발견했다. True : 가장 일반적인 리소스, 특히 메모리 및 열린 파일이 자동으로 해제됩니다. 그러나 OS가 알지 못하는 리소스 (예 : 열린 서버 또는 데이터베이스 연결)는 제대로 종료되지 않았기 때문에 원격에서 열린 상태로 유지 될 수 있습니다. 소켓은 계속 유지됩니다. 최상위 예외 처리기를 사용하는 것이 좋습니다.
피터-모니카 복직 자


2

System.exit (0)을 사용하여 응용 프로그램을 종료하면 마지막으로 실행되지 않습니다. 에서와 같이

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

결과는 다음과 같습니다.


4
당신은 내가 생각하는 잘못된 언어로 대답 c#하고 있습니다 Java. 질문은 관한 것이지만 이것은 것 같습니다 . 그리고 그 외에, 대부분의 경우 System.exit():-) 나쁜 디자인의 힌트
z00l

0

시나리오의 99 %가 finally블록 내부의 코드 가 실행 되도록 보장 하지만이 시나리오를 생각해보십시오 try.-> finally블록 (no catch) 이있는 스레드가 있고 해당 스레드 내에서 처리되지 않은 예외가 발생합니다. 이 경우 스레드가 종료되고 해당 finally블록이 실행되지 않습니다 (이 경우 응용 프로그램을 계속 실행할 수 있음)

이 시나리오는 매우 드물지만 대답이 항상 "예"가 아니며 대부분 "예"이며 때로는 드문 경우에 "아니오"라는 것만 보여줍니다.


0

finally 블록의 주요 목적은 그 안에 기록 된 내용을 실행하는 것입니다. try 또는 catch에서 발생하는 모든 것에 의존해서는 안되지만 System.Environment.Exit (1)을 사용하면 다음 코드 줄로 이동하지 않고 응용 프로그램이 종료됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.