게임 클라이언트 개발자 면접에서 주로 나오는 질문을 보다가 C#에서의 string의 진실에 대해 알 수 있었다. 그래서 이를 글로 정리하려고 한다
프로그래밍을 많이 해 본 사람이라면 흔히 문자열을 나타내는 클래스라고 알고 있을 것이다.
System 네임스페이스에 String 클래스로 선언되어 있고 string 별칭으로 사용할 수 있다. 문자열을 안전하게 사용할 수 있도록 다양한 메서드들을 제공하며 연산자 오버로딩도 제공한다
내부적으로 char의 배열로 구현되어 있어 인덱스를 통해 개별적으로 접근이 가능하다
내가 처음 안 사실은 string이 불변 객체였다는 사실이다. 즉, 메모리에 한번 할당되면 더 이상 변경되지 않는다
유니티에서 개발하다 보면 string을 상당히 많이 쓸텐데 string 객체를 선언하고 자주 변경한 기억이 있다. 사실 이게 기존의 string을 변경하는 것이 아니라 새로 변경한 string 객체를 새로 만드는 것이었다
아니 그럼 계속 변경할 때마다 새로 메모리를 할당하고 이전의 string은 G.C를 통해 회수되고 이런 식이었잖아? 사실 string이 고도로 최적화되어 있어서 성능에 큰 영향을 주지 않는다고 하지만 성능 저하에 여지를 줄 수 있는 것들은 해결해보려고 노력해야 된다고 생각한다
그 문제점의 해결책이 있으며 이따가 설명하겠다
string을 선언하고 초기화하는 방법을 알아보자
// 단순 선언만 -> 이대로 참조하면 null 에러, string 자체를 출력하면 null이 나옴
// 또는 string msg = null;
string msg;
// null이 아닌 빈 스트링 생성
string msg = ""; // 빈 문자열을 이렇게 초기화한 적이 많았는데 이것보다는
// 이 방법이 더 좋다고 한다
// 가독성과 명시성이 더 좋고 성능적으로도 String.Empty가 단일 인스턴스로 정의되어 있어
// 빈 문자열 인스턴스를 생성하는 것이 아니라 참조하는 것이 되어 GC의 부담을 줄일 수 있음
string msg = String.Empty;
// 리터럴로 초기화
string msg = "Hello";
// String 클래스 생성자 사용
char[] chars = { 'H', 'E', 'L', 'L', 'O' };
string msg = new String(chars);
// 형식 문자열 사용
string name = "jaehoon";
string msg = string.Format("Hello, {0}!", name);
// 위 보다 이 방법이 훨씬 편함
// 보간 문자열 사용(C# 6.0 이상)
string name = "jaehoon";
string msg = $"Hello, {name}";
string에도 문자열을 관리하는 다양한 메서드들이 있긴 하지만 이 포스트의 주제와는 잘 맞지 않고 게임 개발하면서 string을 주로 쓰는 게임이 아니면 잘 사용하는 일이 없는 것 같아 따로 적진 않겠지만 다양한 메서드들이 있으니까 한 번씩 보고 개발하다가 '아 이런 게 있지'하고 찾아서 사용하면 좋을 것 같다(코테하려면 외우는 게 좋긴 함. 근데 C#이라서)
이제 앞에서 말한 string의 불변성으로 인한 성능 저하를 해결할 수 있는 친구를 알아보자
이것을 잘 알지는 못했지만 스쳐 지나면서 본 기억은 있다. string을 입력하면서 '이게 뭐지' 하고 지나쳤는데 알게 돼서 반갑다
System.Text 네임스페이스에 정의되어 있는 클래스이며 문자열을 효율적으로 조작할 수 있도록 설계되어 문자열이 자주 변경되는 경우에 유용한 클래스이다
가변 객체이며 문자열을 수정할 때마다 새로운 객체를 생성하는 것이 아니고 기존 객체를 수정한다. 그래서 string보다 문자열을 자주 변경할 때 메모리 할당과 복사를 줄여 더 나은 성능을 제공한다
다만 string보다 지원하는 함수나 연산자 오버로딩도 적음
특징으로는 초기 용량을 작게 설정하면 내부적으로 배열을 여러 번 확장해야 할 수 있어서 비효율적이고 스레드에 안전하지 않아 멀티 스레드의 경우 적절한 동기화가 필요하다
내부적으로 버퍼를 사용해 문자열을 저장해 문자열의 크기가 커지면 버퍼도 확장되어 추가적인 메모리 할당을 발생시킬 수 있어 사용 후에는 StringBuilder를 해제하거나 초기화하는 것이 좋다
// 선언 및 초기화
StringBuilder sb = new StringBuilder();
// string으로 초기화
string msg = "hello";
StringBuilder sb = new StringBuilder(msg);
// 초기 용량 설정
StringBuilder sb = new StringBuilder(50);