break는 루프를 일찍 끝냅니다. continue는 현재 반복의 나머지를 건너뛰고 다음 반복으로 갑니다. 둘 다 자신을 둘러싼 가장 안쪽 루프에 적용되며, for, foreach, while, do while에서 똑같이 동작합니다.
출력:
process order of 45
skip invalid total 0
process order of 120
skip invalid total -5
process order of 80
test order found, stopping
after the loop
continue는 0과 -5에 대해 처리 줄을 건너뛰었습니다. break는 999에서 루프를 멈췄으므로 60은 방문하지 않았고, 실행은 루프 뒤의 첫 문장에서 이어졌습니다.
break: 답을 알면 멈추기
break의 전형적인 용도는 검색입니다. 찾던 것을 찾았다면 나머지를 볼 이유가 없습니다.
출력:
first invalid email at index 1
break 뒤에는 반복자(i++)가 실행되지 않으므로, i를 루프 밖에서 선언했다면 일치한 인덱스가 그대로 남아 있습니다. 간단한 검색이라면 라이브러리 호출이 같은 말을 합니다. Array.FindIndex(emails, e => !e.Contains("@"))는 인덱스(또는 -1)를 반환하고, LINQ의 emails.Any(e => !e.Contains("@"))는 잘못된 것이 있는지 답합니다.
continue: 다음 반복으로 건너뛰기
continue는 루프를 위한 보호 절입니다. 실제 작업을 if로 감싸는 대신, 원하지 않는 경우를 맨 위에서 거부하세요:
출력:
pen costs 1.20
mug costs 8.00
continue 다음에 어디로 가는지는 루프에 따라 다릅니다. for 루프에서는 반복자를 실행한 뒤 조건을 검사합니다. while 루프에서는 바로 조건으로 점프하며, 이것이 전형적인 버그로 이어집니다:
int i = 0;
while (i < 10)
{
if (i % 2 == 0)
continue; // i++ below is skipped: i stays 0 forever
Console.WriteLine(i);
i++;
}
continue 전에 카운터를 갱신하거나, 갱신이 헤더에 있어서 항상 실행되는 for 루프를 쓰세요.
루프 안 switch 안의 break
switch에서 break는 case 섹션을 끝냅니다. 그 switch가 루프 안에 있을 때 break는 루프가 아니라 switch에 속합니다:
출력:
quit received
count = 2
"quit" case에 일반 break를 썼다면 switch만 끝나고 루프는 계속되어 count는 3이 되었을 것입니다. goto는 루프 뒤의 레이블로 점프합니다. 루프를 메서드로 옮기고 그 case에서 return을 써도 되며, 이 경우 레이블이 필요 없습니다.
중첩 루프 빠져나가기
C#에는 Java의 break outer; 같은 레이블 붙은 break가 없습니다. 검색이 두 중첩 루프를 돌 때 안쪽 루프의 break는 안쪽 루프만 끝내고, 바깥 루프는 다음 행으로 넘어갑니다. 표준적인 해결책이 세 가지 있습니다.
1. 바깥 루프가 검사하는 플래그:
출력:
first free seat: row 1, seat 2
2. 두 루프 뒤의 레이블로 가는 goto. 많은 C# 스타일 가이드가 goto를 받아들이는 유일한 곳입니다. "두 루프를 모두 빠져나간다"를 정확히 표현하기 때문입니다:
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (seats[row, col] == 0)
{
Console.WriteLine($"first free seat: row {row}, seat {col}");
goto done;
}
}
}
done:
Console.WriteLine("search finished");
3. return을 쓰는 메서드. 검색에 이름이 생기고 결과를 반환할 수 있어서 보통 가장 깔끔합니다:
출력:
first free seat: row 1, seat 2
C# 7.0은 로컬 함수도 도입했으며, 이 헬퍼 메서드를 호출하는 메서드 안에 둘 수 있게 해 줍니다. 여기서 쓴 out 매개변수 패턴은 ref와 out 페이지에서 설명합니다.
goto와 레이블
레이블은 식별자 뒤에 콜론을 붙인 것이고, goto label;은 그곳으로 점프합니다. 규칙이 엄격해서 최악의 오용을 막아 줍니다:
- 레이블은 같은 메서드 안,
goto와 같은 블록이나 이를 둘러싼 블록에 있어야 합니다. 블록 밖으로는 점프할 수 있지만 블록 안으로는 절대 안 됩니다. 밖에서 루프 본문 안의 레이블로 점프하면 CS0159 오류(goto 문의 범위 안에 그런 레이블이 없음)입니다. goto,break,continue로finally블록을 빠져나갈 수 없습니다.switch안에서goto case 3;과goto default;는 다른 섹션으로 제어를 넘깁니다. 다음 case로 암묵적으로 넘어가는 것은 컴파일 오류이므로, C# switch는 이 방법으로 fall through를 표현합니다.
goto로 루프를 만들 수도 있지만(레이블로 거꾸로 점프), while 루프가 모든 독자가 알아보는 형태로 같은 것을 표현합니다. goto는 중첩 루프 탈출과 switch 섹션에만 쓰세요.
return, throw와 루프
return은 메서드 전체를 빠져나가므로 그 안의 모든 루프도 빠져나갑니다. throw도 예외를 발생시켜 같은 일을 합니다. finally가 있는 try 블록 안에서는 break, continue, goto, return 모두 제어가 넘어가기 전에 finally 블록을 실행하므로, using 블록에서 파일을 열어 둔 루프를 빠져나가도 안전합니다.
| 문장 | 빠져나가는 것 | 이어지는 곳 |
|---|---|---|
continue | 현재 반복 | 다음 반복(반복자, 그다음 조건) |
break | 가장 안쪽 루프 또는 switch | 그 뒤의 문장 |
goto label | 둘러싼 모든 블록 | 레이블 |
return | 메서드 | 호출한 쪽 |
throw | 일치하는 catch까지의 모든 것 | catch 블록 |
자주 묻는 질문
C#에서 break와 continue의 차이는 무엇인가요?
break는 가장 안쪽 루프(또는 switch)를 즉시 끝내고, 실행은 그 뒤에서 이어집니다. continue는 현재 반복만 끝내고 다음 반복으로 점프합니다. for 루프에서는 반복자가 다음에 실행되고, while과 do while에서는 조건이 다음에 검사되며, foreach에서는 다음 요소를 가져옵니다.
C#에서 중첩 루프를 빠져나가려면 어떻게 하나요?
C#에는 레이블 붙은 break가 없습니다. 세 가지 방법이 있습니다. bool 플래그를 설정하고 바깥 루프에서 검사하거나, goto로 두 루프 뒤의 레이블로 점프하거나, 루프를 메서드로 옮기고 거기서 return합니다. 메서드 방식이 보통 가장 깔끔한데, 검색에 이름과 반환값도 생기기 때문입니다.
C#에서 switch 안의 break는 루프를 빠져나가나요?
아니요. 루프 안에 있는 switch에서 break는 switch 섹션만 끝내고 루프는 계속됩니다. case 안에서 루프를 빠져나가려면 return, switch 뒤에서 검사하는 플래그, 또는 루프 뒤 레이블로의 goto를 쓰세요. case 안의 continue는 루프의 다음 반복으로 갑니다.
C#에서 goto를 써도 되나요?
네. goto label;은 같은 메서드의 label:로 점프하지만, 블록 안으로 들어가거나 finally 밖으로 점프할 수는 없습니다. switch 안에서는 goto case value;와 goto default;가 섹션 사이를 점프합니다. 중첩 루프 탈출과 명시적인 switch fall through 말고는 goto가 가장 명확한 선택인 경우가 드뭅니다.