본문 바로가기
책/Effective C#

Effective C# - Item4 : string.Format()을 보간 문자열로 대체하라

by 코딩하는 돼징 2023. 11. 10.
반응형

string.Format()

특정 형식을 가진 문자열을 만들기 위해 포맷 문자열과 해당 문자열에 삽입될 인자들을 사용한다. 하지만 이 메서드가 가진  한계들로 인해 코드 작성 및 디버깅이 어려워 질 수 있다.

 

01 포맷 문자열과 인자 리스트의 분리

string.Format()메서드에서 포맷 문자열과 인자 리스트는 분리되어 전달되기 때문에 코드상에서 포맷 문자열과 인자들의 관계를 시각적으로 파악하기 어려울 수 있다. 이로 인해 어떤 형태의 문자열이 생성되는지 코드를 실행하지 않고서는 쉽게 짐작하기 어렵다.

 

아래 예시를 보면 example은 포맷 문자열이며 string,Format()과 분리되어 있음을 확인할 수 있다. 그러므로 코드를 읽는 사람이나 개발자는 example을 보고 실제 어떤 값이 들어가는지 확인할 수 있다. 

string example = "{0} is {1} years old and works as a {2}.";
string result = string.Format(example, "piggy", 100, "tistory");

결과적으로 실행전까지 어떤 형태의 문자열이 생성되는지 정확히 파악하기 어렵다.


02 인자의 일치 여부 확인 미지원

string.Format()메서드는 포맷 문자열에 나타낸 인자의 개수와 실제 전달되는 인자의 개수가 일치하는지 런타임에서 확인하지 않는다. 따라서 포맷 문자열에 필요한 인자를 누락하면 런타임에 예외가 발생한다.

 

아래에 example은 3개의 인덱스를 가지고 있지만 string.Fomrat()메서드에는 두 개의 인자만 전달된다.

string example = "{0} is {1} years old and works as a {2}.";
string result = string.Format(example, "piggy", "tistory");

그러므로 아래와 같이 FormatException이 발생한다.

System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.

보간 문자열(interpolated string)

문자열을 더 직관적으로 작성하고 가독성을 높이기 위한 기능으로, 보간 문자열을 사용하면 문자열을 생성할 때 변수나 표현식을 쉽게 삽입할 수 있다. 

 

일반적으로 문자열 보간은 문자열 앞에 $ 기호를 붙이고 중괄호 {} 내에 변수나 표현식을 넣어 사용한다. 

Console.WriteLine($"The value of pi is {Math.PI}");

성능적인 측면 고려

문자열 보간을 사용하더라도 실제로는 컴파일러가 내부적으로 string.Format()와 유사한 방식으로 코드를 생성한다. 그러므로 object배열을 전달하는 기존 포메팅 함수를 호출하도록 코드가 생성된다.

01 박싱(boxing)

값 타입(int,double 등)은 object 타입으로 변환하기 위해 박싱이 필요하다. 이는 값 타입을 힙 메모리에 복사하는 과정을 의미하며 이로인해 성능 저하가 발생할 수 있다.

int age = 42;
// 박싱 발생: 값 타입인 int가 object로 변환됨
object boxed = age;
// 언박싱: object를 다시 int로 변환
int unboxed = (int)boxed;

02 문자열 보간의 컴파일러 동작

컴파일러는 문자열 보간 표현식 내부의 각 변수나 표현식을 문자열로 변환하여 string.Format()과 유사한 형태로 코드를 생성한다.

03 박싱을 피하는 최적화

반복적으로 문자열을 생성하는 상황이나 성능이 중요한 부분에서는 값 타입의 박싱을 피하는 것이 좋다. 이를 위해 값 타입을 명시적으로 문자열로 변환하고 그 값을 문자열 보간 넣는 것이 성능에 도움이 된다.

Console.WriteLine($"The value of pi is {Math.PI.ToString()}");

특정 형식으로 만들기

01 ToString 메서드

간단하게 특정 형식을 명시적으로 제어할 수 있다. "F2"는 소수점 아래 둘째 자리가까지의 고정 소수점 형식을 나타낸다.

Console.WriteLine($"The value of pi is {Math.PI.ToString("F2")}");

02 문자열 보간 

 

Console.WriteLine($"The value of pi is {Math.PI:F2}");

주의 해야할 경우

01  @ 기호를 사용한 verbatim string literal

문자열 보간에서는 {} 중괄호 안에서 사용되는 콜론(:)이 특정 포맷을 나타내기 때문에, 조건 표현식에서의 콜론과 혼동이 발생할 수 있다. 따라서 조건 표현식을 문자열 보간과 함께 사용할 때는 적절한 괄호를 사용하여 그룹화해주어야 다.

bool condition = true;
int value = 42;

// 콜론이 특정 포맷을 나타내기 때문에 혼동 발생
Console.WriteLine($"Result: {condition ? "Valid" : "Invalid"} Value: {value:F2}");

자열 보간의 중괄호 안에서 :F2를 사용하여 숫자 형식을 지정하고 있다. 그러나 이렇게 사용된 콜론은 문자열 보간에서의 포맷 지정 구문으로 인식되어 조건 표현식의 콜론과 혼동이 발생할 수 있다.

 

 

bool condition = true;
int value = 42;

// @ 기호 사용하여 verbatim string literal
Console.WriteLine($@"Result: {(condition ? "Valid" : "Invalid")} Value: {value:F2}");

 @ 기호를 사용한 verbatim string literal은 문자열 내의 이스케이프 문자를 무시하고 그대로 표현되므로 문자열 보간에서의 콜론(:)이 특정 포맷을 나타내는 것과 상관없이 문자열 그대로 인식된다. 따라서 문자열 보간과 조건 표현식에서의 콜론이 명확하게 구분되어 코드가 혼동 없이 동작한다.

 

 


02 null값 체크

값이 누락된 경우 명확하게 처리하기 위해서 null 조건 연산자와 null 병합 연산자를 함께 사용

Console.WriteLine($"The custoner's name is {c?.Name ?? "Name is missing"}");

c?.Name : c객체가 null이 아닌 경우에만 Name속성에 접근한다. 만약 c가 null이면 이 부분은 null을 반환한다.

?? : null 병합 연산자는 왼쪽 피연산자가 null이아니면 왼쪽 피연산자를 null이면 오른쪽 피연산자를 변환한다.

 

보간 문자열의 표현식은 중첩해서도 사용할 수 있다. {}문자 사이의 모든 구문은 C#코드의 일부인 동시에 표현식으로 간주된다.


결론

어떤 경우라도 문자열 보간 기능의 결과가 문자열이라는 사실은 잊어서는 안된다. 문자열 보간 기능은 기존의 C언어에서 사용하던 방식에서의 오류를 줄일 수 있고 더욱 강력할 뿐만 아니라 활용도 또한 매우 높은 기술이므로 보간 문자열을 사용하자

 

 

 

 

본 게시글은 Effective C#을 읽고 정리하였습니다.

 

 

 

반응형

댓글