nameof 식
nameof 식 (C# 참조)
어떤 기호의 이름을 문자열로 그대로 써야 할 때가 있어요. 예를 들어 예외 메시지에 변수 이름을 넣는다거나, 로그에 속성 이름을 남긴다거나 할 때죠. 그 이름을 하드코딩하면 나중에 리팩터링했을 때 문자열만 남아 헤매기 쉬운데, nameof 식을 쓰면 그 문제를 깔끔하게 해결해요. nameof는 변수·형식·멤버의 이름을 문자열 상수로 만들어 주는데, 컴파일 시점에 평가되어 런타임에서는 아무 영향도 없어요. 피연산자가 형식이나 네임스페이스일 때 만들어지는 이름은 정규화(fully qualified)되지 않는다는 점도 기억해 두면 좋아요.
본문
실제 사용 예를 먼저 볼게요. List<>를 쓴 부분은 C# 14 이상에서 지원되니 참고하세요.
Console.WriteLine(nameof(System.Collections.Generic)); // output: Generic
Console.WriteLine(nameof(List<int>)); // output: List
Console.WriteLine(nameof(List<>)); // output: List
Console.WriteLine(nameof(List<int>.Count)); // output: Count
Console.WriteLine(nameof(List<int>.Add)); // output: Add
List<int> numbers = new List<int>() { 1, 2, 3 };
Console.WriteLine(nameof(numbers)); // output: numbers
Console.WriteLine(nameof(numbers.Count)); // output: Count
Console.WriteLine(nameof(numbers.Add)); // output: Add
nameof의 피연산자로는 List<>나 Dictionary<,>처럼 형식 인자를 채우지 않은 바인딩되지 않은(unbound) 제네릭 형식도 쓸 수 있어요. 이때 결과는 산수(arity)나 형식 인자 목록이 빠진 단순한 형식 이름이 됩니다 — nameof(List<>)는 "List"를 반환하지요. 이런 바인딩되지 않은 제네릭 피연산자는 로그나 진단 메시지, 특성(attribute) 인자에서 제네릭 형식 이름 자체는 중요하지만 형식 인자는 중요하지 않을 때 유용해요.
nameof는 인자 검증 코드를 더 유지보수하기 좋게 만들어 주기도 해요. 값이 null일 때 함께 던질 변수 이름을 그대로 쓰고 있죠.
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), $"{nameof(Name)} cannot be null");
}
메서드나 그 매개변수에 붙이는 특성 안에서도 메서드 매개변수를 nameof로 참조할 수 있어요. 아래 코드는 메서드의 특성, 로컬 함수, 람다 식의 매개변수 각각에 어떻게 쓰는지 보여 줍니다.
[ParameterString(nameof(msg))]
public static void Method(string msg)
{
[ParameterString(nameof(T))]
void LocalFunction<T>(T param) { }
var lambdaExpression = ([ParameterString(nameof(aNumber))] int aNumber) => aNumber.ToString();
}
이렇게 매개변수에 nameof를 쓰는 패턴은 nullable 분석 특성이나 CallerArgumentExpression 특성을 쓸 때 특히 유용해요.
피연산자가 축자 식별자(verbatim identifier)라면 그 이름에 @ 문자는 포함되지 않아요. 예를 들어 결과는 "@new"가 아니라 "new"가 됩니다.
var @new = 5;
Console.WriteLine(nameof(@new)); // output: new
자세한 문법 규칙이 궁금하다면 C# 언어 사양의 Nameof expressions 섹션을 참고하세요.