흔히 삼항 연산자라고 부르는 조건 연산자는 하나의 식 안에서 두 값 중 하나를 고릅니다: condition ? valueIfTrue : valueIfFalse.
출력:
0 items, shipping 4.99
1 item, shipping 4.99
5 items, shipping 0
조건은 bool이어야 합니다. 조건을 평가한 뒤 두 분기 중 하나만 실행되고, 다른 하나는 절대 평가되지 않습니다. 그래서 list.Count > 0 ? list[0] : "empty"처럼 빈 리스트에서 list[0]을 평가하면 예외가 나는 보호된 접근에도 안전합니다.
삼항 연산자와 if/else
삼항 연산자는 식입니다. 대입하거나, 넘기거나, 반환하는 값을 만듭니다. if 문은 문장을 실행합니다. 다음 두 메서드는 같은 일을 합니다:
// with if/else
static string Status(int stock)
{
if (stock > 0)
return "in stock";
else
return "sold out";
}
// with the ternary
static string Status(int stock) => stock > 0 ? "in stock" : "sold out";
두 분기가 같은 종류의 짧은 값일 때 삼항 연산자를 쓰세요. 분기가 여러 문장을 실행하거나, 부수 효과가 있거나, 조건이 길어서 한 줄에 편하게 들어가지 않을 때는 if를 쓰세요.
동작만 수행하는 if는 삼항 연산자로 대체할 수 없습니다. 다음은 컴파일되지 않습니다:
stock > 0 ? Ship(order) : Refund(order);
// error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
else 없는 삼항 연산자도 없습니다. 식은 두 경우 모두 값을 가져야 하므로 ?와 :는 항상 필요합니다.
두 분기에는 공통 타입이 필요합니다
컴파일러는 두 분기로부터 식 전체의 타입을 결정합니다. 한 분기가 다른 분기의 타입으로 암시적으로 변환되어야 합니다. 어느 쪽도 안 되면 CS0173이 발생합니다:
var discount = isMember ? 10 : null;
// error CS0173: Type of conditional expression cannot be determined because
// there is no implicit conversion between 'int' and '<null>'
var label = inStock ? 1 : "none";
// error CS0173: ... between 'int' and 'string'
해결책은 원하는 타입으로 한 분기를 캐스트해서 다른 분기가 그 타입으로 변환되게 하는 것입니다:
출력:
no discount
7
3.5
points / 2.0 : 0에는 캐스트가 필요 없습니다. 0은 int이고 double로 암시적 변환되므로 식은 double이 됩니다.
C# 9는 대상 타입 지정을 추가했습니다. 식이 선언된 타입에 대입되면 컴파일러가 그 타입을 씁니다. int? discount = isMember ? 10 : null;은 C# 9 이상에서 컴파일됩니다. var에는 대상 타입이 없으므로 var discount = isMember ? 10 : null;은 모든 버전에서 여전히 실패합니다.
우선순위와 괄호
조건 연산자의 우선순위는 +, ==, &&보다 낮은, 아주 낮은 순위입니다. a > b ? a : b에서는 편리하지만 문자열 이어 붙이기 안에서는 놀랍게 동작합니다:
Console.WriteLine("Status: " + isActive ? "on" : "off");
// error CS0029: Cannot implicitly convert type 'string' to 'bool'
컴파일러는 조건을 문자열인 "Status: " + isActive로 읽습니다. 삼항 연산자를 괄호로 감싸면 해결됩니다: "Status: " + (isActive ? "on" : "off").
문자열 보간에는 고유한 함정이 있습니다. { } 안에서 콜론은 서식 지정자({price:F2})를 시작하므로, 괄호 없는 삼항 연산자는 파서를 혼란스럽게 합니다. 괄호로 감싸세요:
출력:
2 users online
You have 1 unread message
Status: online
중첩 삼항 연산자
이 연산자는 오른쪽 결합이므로 a ? x : b ? y : z는 a ? x : (b ? y : z)로 묶입니다. 조건을 한 줄에 하나씩 배치한 짧은 체인은 읽을 만합니다:
출력:
512 B
20 KB
5 MB
두세 단계를 넘거나 조건이 서로 다른 것을 검사한다면 if/else if나 switch 식으로 바꾸세요. 참 분기 안에 중첩하는 형태(a ? (b ? x : y) : z)가 가장 먼저 따라가기 어려워집니다.
??와 ?.가 흔한 삼항 연산자를 대체합니다
많은 삼항 연산자는 null을 처리하기 위해서만 존재합니다. C#에는 이를 위한 더 짧은 연산자가 있습니다:
| 삼항 연산자 | 짧은 형태 | 의미 |
|---|---|---|
name != null ? name : "guest" | name ?? "guest" | null일 때 대체값 |
customer != null ? customer.Email : null | customer?.Email | null을 허용하는 멤버 접근 |
c != null && c.Address != null ? c.Address.City : "unknown" | c?.Address?.City ?? "unknown" | 둘을 조합 |
value != null ? value : throw new ArgumentNullException(...) | value ?? throw new ArgumentNullException(...) | 빠른 실패 |
출력:
Lena from Berlin
guest from unknown
guest from unknown
throw 식은 삼항 연산자의 어느 분기에도 쓸 수 있습니다: int age = input >= 0 ? input : throw new ArgumentOutOfRangeException(nameof(input));.
ref 조건식
C# 7.2부터 삼항 연산자는 두 값이 아니라 두 변수 중 하나를 고를 수 있고, 결과를 통해 대입할 수 있습니다:
// C# 7.2 and later
int wins = 0, losses = 0;
bool won = true;
ref int counter = ref (won ? ref wins : ref losses);
counter++; // increments wins
두 분기 모두 같은 타입의 변수에 대한 ref여야 합니다. 애플리케이션 코드에서는 드물며, 주로 큰 구조체나 배열을 복사하지 않고 다루는 성능 중심 코드를 위해 존재합니다.
자주 묻는 질문
C#의 삼항 연산자란 무엇인가요?
조건 연산자 condition ? valueIfTrue : valueIfFalse로, 피연산자가 세 개인 유일한 C# 연산자입니다. 조건을 평가한 뒤 두 값 중 정확히 하나만 평가해 반환합니다: string label = age >= 18 ? "adult" : "minor";.
C#에서 else 없이 삼항 연산자를 쓸 수 있나요?
아니요. 식은 항상 값을 만들어야 하므로 ?와 : 부분이 모두 필요합니다. 조건이 참일 때만 무언가를 하고 싶다면 if 문을 쓰세요. 삼항 연산자는 단독 문장이 될 수도 없습니다. x > 0 ? A() : B();는 CS0201 오류입니다.
"Type of conditional expression cannot be determined" 오류가 나는 이유는 무엇인가요?
두 분기에 공통 타입이 없기 때문입니다. 예를 들어 var x = found ? 42 : null;(int와 null)이나 found ? 1 : "none"입니다. 타입이 존재하도록 한 분기를 캐스트하세요: found ? (int?)42 : null. C# 9부터는 선언된 대상 타입도 동작합니다. int? x = found ? 42 : null;은 컴파일되지만 var는 여전히 실패합니다.
문자열 보간 안에서 삼항 연산자는 어떻게 쓰나요?
괄호로 감싸세요: $"{(count == 1 ? "item" : "items")}". 괄호가 없으면 :이 서식 지정자의 시작으로 읽혀서 코드가 컴파일되지 않습니다.
C#에서 삼항 연산자를 중첩할 수 있나요?
네. 이 연산자는 오른쪽 결합이므로 a ? x : b ? y : z는 a ? x : (b ? y : z)라는 뜻입니다. 한 단계 중첩을 여러 줄에 나눠 쓰면 읽을 만하지만, 더 깊은 체인은 if/else if 체인이나 switch 식이 더 명확합니다.