문제

.NET에서 문자열은 불변이며 기준 유형 변수입니다. 이것은 종종 동작으로 인해 가치 유형 객체에 대해 착각 할 수있는 새로운 .NET 개발자에게 놀라운 일입니다. 그러나 사용 관행 외에 StringBuilder 오랫동안 연결을 위해. 루프에서 실제로이 차이를 알아야 할 이유가 있습니까?

.NET 문자열과 관련하여 가치 참조 차이를 이해함으로써 어떤 실제 시나리오가 도움이됩니까?

도움이 되었습니까?

해결책

의 디자인 stringS는 의도적으로 프로그래머로서 그것에 대해 너무 걱정할 필요가 없도록 의도적으로있었습니다. 많은 상황에서 이것은 문자열에 대한 다른 참조가 존재하고 동시에 (객체 참조와 같이) 변경 될 경우 가능한 복잡한 결과를 너무 많이 생각하지 않고도 할당, 이동, 복사, 문자열을 변경할 수 있음을 의미합니다.

메소드 호출의 문자열 매개 변수

(편집 :이 섹션은 나중에 추가되었습니다)
문자열이 방법으로 전달되면 참조로 전달됩니다. 그들이 방법 본문에서만 읽히면 특별한 일은 일어나지 않습니다. 그러나 변경되면 사본이 생성되고 임시 변수가 나머지 방법에 사용됩니다. 이 과정이 호출됩니다 복사기.

주니어 문제는 객체가 참조이며 전달 된 매개 변수를 변경하는 메소드에서 변경된다는 사실에 익숙하다는 것입니다. 문자열과 똑같이하기 위해서는 ref 예어. 실제로 문자열 참조를 변경하고 호출 함수로 반환 할 수 있습니다. 그렇지 않은 경우 방법 본문에 의해 문자열을 변경할 수 없습니다.

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

StringBuilder에

이 차이는 중요하지만 초보자 프로그래머는 일반적으로 그것에 대해 너무 많이 알지 못하는 것이 좋습니다. 사용 StringBuilder 많은 현악기를 할 때 "빌딩"은 좋지만 종종 응용 프로그램은 더 많은 물고기가 튀김과 작은 성능 이득을 갖게됩니다. StringBuilder 무시할 수 있습니다. 당신에게 그것을 말하는 프로그래머에주의하십시오 모두 문자열 조작은 StringBuilder를 사용하여 수행해야합니다.

매우 대략적인 경험 법칙 : StringBuilder는 창조 비용이 약간 있지만 추가는 저렴합니다. 문자열은 저렴한 생성 비용을 가지고 있지만 연결은 비교적 비쌉니다. 전환점은 크기에 따라 약 400-500의 연결입니다. 그 후 StringBuilder가 더 효율적입니다.

StringBuilder 대 스트링 성능에 대한 자세한 내용

편집 : Konrad Rudolph의 의견을 바탕 으로이 섹션을 추가했습니다.

이전 경험 법칙이 궁금한 점이 있다면 다음과 같은 자세한 설명을 고려하십시오.

  • 많은 작은 문자열이있는 StringBuilder는 Outruns String 연결이 다소 빠르게 연결되지만 (30, 50 추가), 2µs에서는 100% 성능 이득조차도 종종 무시할 수 있습니다 (일부 드문 상황에 안전 함).
  • 큰 문자열이있는 StringBuilder (80 문자 이상)는 수천 명, 때로는 수십만 개의 반복 이후에만 문자열 연결을 능가하며, 그 차이는 종종 몇 가지 지각 일뿐입니다.
  • 문자열 액션 (교체, 삽입, 하위 문자열, 정규 등)을 혼합하면 종종 StringBuilder 또는 String 연결을 사용하여 동일하게 만듭니다.
  • 상수의 문자열 연결 상수는 컴파일러, CLR 또는 JIT에 의해 최적화 될 수 있으며, StringBuilder는 할 수 없습니다.
  • 코드는 종종 연결을 혼합합니다 +, StringBuilder.Append, String.Format, ToString 그러한 경우 StringBuilder를 사용하는 다른 문자열 작업은 거의 효과적이지 않습니다.

그렇게 할 때 ~이다 효율적입니까? 많은 작은 문자열이 추가되는 경우, 즉, 예를 들어 데이터를 파일에 직렬화하고 "작성된 데이터"를 StringBuilder로 변경할 필요가없는 경우. StringBuilder가 기준 유형이고 문자열이 변경 될 때 문자열이 복사되기 때문에 많은 방법이 무언가를 추가 해야하는 경우.

인턴 된 현에

주니어 프로그래머에게도 문제가 발생하고 참조 비교를 시도하고 때로는 결과가 사실이며 때로는 같은 상황에서는 거짓임을 알게 될 때 문제가 발생합니다. 무슨 일이에요? 문자열이 컴파일러에 의해 인턴되고 글로벌 정적 인턴 된 문자열 풀에 추가되면, 두 줄 사이의 비교는 동일한 메모리 주소를 가리킬 수 있습니다. (참조!) 두 개의 동등한 줄을 비교할 때, 하나는 인턴과 다른 하나를 비교하면 거짓이됩니다. 사용 = 비교, 또는 Equals 그리고 함께 놀지 마십시오 ReferenceEquals 줄을 다룰 때.

string.empty

같은 리그에서는 때때로 사용할 때 발생하는 이상한 행동에 적합합니다. String.Empty: 정적 String.Empty 항상 인턴이지만 할당 된 값이있는 변수는 아닙니다. 그러나 기본적으로 컴파일러가 지정됩니다 String.Empty 동일한 메모리 주소를 가리 킵니다. 결과 : 비교할 때 변한 문자열 변수 ReferenceEquals, 대신 거짓을 기대할 수있는 동안 true를 반환합니다.

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

깊이

당신은 기본적으로 시작되지 않은 사람들에게 어떤 상황이 발생할 수 있는지에 대해 물었습니다. 나는 내 요점이 피하기 위해 귀결된다고 생각합니다 object.ReferenceEquals 문자열과 함께 사용하면 신뢰할 수 없기 때문입니다. 그 이유는 문자열 인턴이 코드에서 문자열이 일정하지만 항상 그런 것은 아니기 때문입니다. 이 행동에 의존 할 수 없습니다. 그렇지만 String.Empty 그리고 "" 컴파일러가 값이 변할 수 있다고 생각하는 경우가 아닙니다. 다른 최적화 옵션 (디버그 대 릴리스 및 기타)은 다른 결과를 산출합니다.

언제 하다 당신은 필요합니다 ReferenceEquals 그래도? 객체가 있으면 의미가 있지만 문자열은 그렇지 않습니다. 또한 문자열을 사용하는 사람은 이해하지 않는 한 사용을 피하기 위해 가르칩니다. unsafe 그리고 고정 된 물체.

성능

성능이 중요하면 문자열이 실제로 ~ 아니다 불변과 그 사용 StringBuilder ~이다 ~ 아니다 항상 가장 빠른 접근 방식입니다.

내가 여기서 사용한 많은 정보가 자세히 설명되어 있습니다. 이 훌륭한 기사에서 현악기, 문자열 조작을 위해 "방법"과 함께 (Mutable Strings).

업데이트: 추가 코드 샘플
업데이트: '깊이'섹션을 추가했습니다 (누군가가 유용하다고 생각하기를 바랍니다.)
업데이트: String Params에 일부 링크가 추가되었습니다
업데이트: 문자열에서 StringBuilder로 전환 할시기에 대한 추정 추가
업데이트: Konrad Rudolph의 발언 후 StringBuilder 대 String Performance에 추가 섹션이 추가되었습니다.

다른 팁

대부분의 코드에서 실제로 중요한 유일한 차이점은 null 문자열 변수에 할당 할 수 있습니다.

불변의 클래스는 모든 일반적인 상황에서 가치 유형처럼 행동하며 차이에 대해 많은 관심을 갖지 않고 많은 프로그래밍을 할 수 있습니다.

당신이 조금 더 깊이 파고 들어서 성능에 관심을 갖는 것은 당신이 구별에 실제로 사용하는 것입니다. 예를 들어, 문자열을 매개 변수로 전달하면 문자열의 사본이 작성된 것처럼 작용하지만 복사는 실제로 발생하지 않습니다. 이것은 문자열이 실제로 가치 유형 (VB6?) 인 언어에 익숙한 사람들에게 놀라운 일이 될 수 있으며 매개 변수로 많은 문자열을 전달하는 것은 성능에 좋지 않을 것입니다.

끈은 특별한 품종입니다. 이들은 참조 유형이지만 대부분의 코더가 값 유형으로 사용합니다. 불변을 만들고 인턴 풀을 사용함으로써 메모리 사용량을 최적화하여 순수한 가치 유형 인 경우 크게 나타납니다.

더 많은 독서 :
C# .NET 문자열 객체는 실제로 참조 별입니까? 그렇게
MSDN의 String.intern 메소드
MSDN의 문자열 (C# 참조)

업데이트:
참조하십시오 abel이 게시물에 대한 의견. 내 오해의 소지가있는 진술을 수정했습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top