반응형
좋은 질문이에요! 말씀하신 생성자에서의 ??는 **null 병합 연산자(null-coalescing operator)**입니다. 코드 조각을 기준으로 교재식으로 정리해 드릴게요.
1) 왜 생겼는가 — “null이면 대체값을 쓰자”
메서드 인자나 계산 결과가 null일 수도 있는 상황에서, 매번 if (x == null) ...를 쓰면 코드가 장황해집니다. 그래서 *“좌변이 null이면 우변을 대신 쓰자”*라는 패턴을 한 줄로 쓰기 위해 ??가 생겼습니다.
2) 무엇인가 — left ?? right의 의미
- 정의: left가 null이 아니면 left를, null이면 right를 반환합니다.
- 평가 순서/성능: left를 먼저 평가하고, left가 null이 아닐 때는 right는 평가하지 않습니다(지연 평가).
→ DateTime.Now 같은 값은 필요할 때만 호출돼요.
당신의 코드에서
Date = now ?? DateTime.Now;
LotID = lotId ?? string.Empty;
- now는 DateTime?(nullable).
- now에 값이 있으면 그 값을 Date로 사용(이때 결과 타입은 DateTime).
- now가 null이면 그때만 DateTime.Now를 호출해 현재 시각을 사용.
- lotId는 string(참조 형식).
- lotId가 null이면 빈 문자열(string.Empty)을 사용.
- ""(빈 문자열)는 null이 아님에 주의! (아래 실수 항목 참고)
3) 언제/어떻게 쓰는가 — 사용 패턴과 MWE
3-1. 파라미터 방어 코드
public LogEntry(eLog category, string message, string lotId = "", int cam = -1, DateTime? now = null)
{
Date = now ?? DateTime.Now; // now가 없으면 현재 시각
Category = category;
Message = message;
LotID = lotId ?? string.Empty; // lotId가 null이면 빈 문자열
Cam = cam;
}
- lotId는 기본값이 ""라 호출자가 아무 것도 안 주면 빈 문자열이 들어옵니다.
다만 호출자가 명시적으로 null을 넘기는 경우를 대비해 ?? string.Empty로 방어하는 셈입니다.
3-2. 간단 진리표 느낌
표현식 결과
null ?? X | X |
A ?? X (A가 null 아님) | A |
someNullable ?? defaultValue | null이면 기본값, 아니면 원래 값 |
3-3. 실전 Mini Example
DateTime? overrideTime = null;
DateTime createdAt = overrideTime ?? DateTime.Now; // overrideTime 없으면 지금 시각
string? userInput = GetTextOrNull(); // 외부 입력
string safeText = userInput ?? string.Empty; // null 방지
참고: 값이 “없음(null)”과 “비어있음(예: "" 또는 0)”은 다른 의미예요. ??는 오직 null을 처리합니다.
4) 자주 하는 실수와 베스트 프랙티스
흔한 실수
- 빈 문자열과 null을 혼동
- "" ?? "fallback"은 "" 그대로입니다(빈 문자열은 null이 아니므로).
- 빈 문자열도 대체하고 싶다면 string.IsNullOrEmpty(lotId) ? "fallback" : lotId처럼 처리하세요.
- 우변 부작용/비용 간과
- left ?? Expensive()에서 left가 null이 아니면 Expensive()는 호출되지 않습니다. 반대로 null일 때만 호출되므로, 우변에 무거운 연산을 둬도 안전합니다.
- ??와 ??= 헷갈림
- x ?? y는 “평가”만,
- x ??= y는 “x가 null이면 y를 대입”입니다.
lotId ??= string.Empty; // lotId가 null일 때만 빈 문자열로 설정
베스트 프랙티스
- 입력 경계에서 일찍 정규화: 생성자/팩토리에서 ??로 null을 처리해, 나머지 도메인 로직에서 null 분기를 최소화하세요.
- 의미 분리: “없음(null)”과 “비어있음(빈 문자열/0)”을 구분해 모델링하고, 필요 시 string.IsNullOrWhiteSpace로 보강.
- NRT(Nullable Reference Types)와 함께 사용: string?를 허용하되, 생성자 초기에 ??로 정규화하면 호출 계약이 명확해집니다.
반응형
'C#' 카테고리의 다른 글
“C# readonly: 생성 후 불변을 보장해 버그를 줄이고 성능을 챙기는 설계 키워드” (0) | 2025.10.10 |
---|---|
“IDisposable: 가비지 컬렉터가 다 못하는 ‘자원 반납’을 결정적으로 보장하는 계약” (0) | 2025.10.10 |
“C# 값 형식에도 없음(null)을 — T? = Nullable<T> 제대로 쓰는 법” (0) | 2025.10.10 |
error. 이벤트핸들러 이름 충돌 (0) | 2025.09.22 |
volatile는 멀티스레드에서 “가벼운 동기화”를 위한 읽기/쓰기 가시성 보장 장치 (0) | 2025.09.19 |
왜 IDisposable을 구현하나요? (0) | 2025.09.19 |
“직접 델리게이트 콜백” → “.NET 표준 이벤트 패턴(Event + EventArgs)” (0) | 2025.09.19 |
sealed를 왜 써야 할까? (0) | 2025.09.19 |