programing

.NET 문자열이 불변인 이유는 무엇입니까?

bestprogram 2023. 8. 15. 11:19

.NET 문자열이 불변인 이유는 무엇입니까?

우리 모두가 알다시피, String은 불변입니다.String이 불변인 이유와 String Builder 클래스가 불변인 이유는 무엇입니까?

  1. 불변 유형의 인스턴스는 스레드가 수정할 수 없으므로 스레드가 다른 스레드와 간섭하는 방식으로 수정할 위험이 제거됩니다(참조 자체는 다른 문제입니다).
  2. 마찬가지로, 앨리어싱은 변경 사항을 생성할 수 없다는 사실(x와 y가 모두 x로 변경된 동일한 개체를 참조하는 경우 y로 변경이 수반됨)은 상당한 컴파일러 최적화를 허용합니다.
  3. 메모리 절약 최적화도 가능합니다.우리가 같은 원리의 다른 버전을 할 수 있지만, 인터닝과 아토마이즈는 가장 명백한 예입니다.한 번은 불변 객체를 비교하고 참조를 중복으로 대체하여 동일한 인스턴스를 가리키도록 함으로써 약 0.5GB의 메모리를 절약한 적이 있습니다(시간이 많이 걸리지만 문제의 경우에는 1분만 더 시작하면 성능이 향상됨).할 수 없는 가변적인 물체로 말입니다.
  4. 다음과 같은 경우가 아닌 한 변수에 불변 유형을 전달하는 것으로 인한 부작용은 발생할 수 없습니다.out또는ref(객체가 아니라 참조가 변경되기 때문에).그러므로 프로그래머는 다음과 같은 경우를 알고 있습니다.string x = "abc" 않고, 방을시작때할, 리그방본변서않다하지습, 러면그니에문의법은것법, 그고▁at면▁then▁of그러▁body.x == "abc"그 방법의 마지막에
  5. 개념적으로, 의미론은 가치 유형에 더 가깝습니다. 특히 평등은 정체성보다는 상태에 기초합니다.은 즉을 합니다."abc" == "ab" + "c"이것이 불변성을 필요로 하지는 않지만, 그러한 문자열에 대한 참조가 그 수명 동안 항상 "abc"와 같다는 사실은 이전 값에 대한 동일성을 유지하는 것이 중요하고 정확성을 보장하기 위해 훨씬 더 쉽게 키로 사용됩니다(문자열은 실제로 일반적으로 키로 사용됩니다).
  6. 개념적으로, 불변하는 것이 더 말이 될 수 있습니다.크리스마스에 한 달을 더하면, 우리는 크리스마스를 바꾸지 않고, 1월 말에 새로운 날짜를 만들었습니다.그러므로 그것은 이치에 맞습니다.Christmas.AddMonths(1)새로운 것을 생산합니다.DateTime변경 가능한 이름을 변경하는 것보다. (다른 예로, 만약 내가 변경 가능한 개체로서 내 이름을 변경한다면, 내가 어떤 이름을 사용하고 있는지, "Jon"은 변경되지 않고 다른 Jon은 영향을 받지 않습니다.
  7. 복제는 수 .return this복사본은 변경할 수 없으므로 어떤 것이 자신의 복사본인 것처럼 가장하는 것이 안전합니다.
  8. [편집, 이것을 잊어버렸습니다.]내부 상태를 개체 간에 안전하게 공유할 수 있습니다.예를 들어, 어레이, 시작 인덱스 및 개수로 지원되는 목록을 구현하는 경우 하위 범위를 생성할 때 가장 비용이 많이 드는 부분은 개체를 복사하는 것입니다.그러나 변경할 수 없는 경우 하위 범위 개체는 동일한 배열을 참조할 수 있으며, 시작 인덱스와 카운트만 변경해야 하며 구성 시간이 상당히 변경됩니다.

전반적으로, 그들의 목적의 일부로 변화를 겪지 않는 물체의 경우, 불변하는 것에는 많은 이점이 있을 수 있습니다.주요 단점은 종종 과장되기는 하지만 추가적인 구성이 필요하다는 것입니다(StringBuilder가 고유한 구성으로 동일한 일련의 연결보다 효율적이 되기 전에 몇 가지 추가 작업을 수행해야 합니다).

때때로 유용할 수도 있지만(많은 웹 및 기타 상태 비저장 응용 프로그램에서 읽기 작업을 수행하는 코드는 업데이트를 수행하는 코드와 별개이며 다른 개체 m을 사용하는 코드는 별개임에도 불구하고) 가변성이 개체의 목적의 일부라면 단점이 될 수 있습니다.자연스러워라 - 나는 객체를 불변으로 만든 다음 그 패턴을 강제로 적용하지 않을 것이지만, 만약 내가 이미 그 패턴을 가지고 있다면 나의 "읽기" 객체를 성능 및 정확성 보장 이득에 대해 불변으로 만들 수도 있습니다.

쓰기 시 복사는 중간 단계입니다.여기서 "실제" 클래스는 "상태" 클래스에 대한 참조를 보유합니다.상태 클래스는 복사 작업에서 공유되지만 상태를 변경하면 상태 클래스의 새 복사본이 만들어집니다.이것은 C#보다 C++과 함께 더 자주 사용되기 때문에 std:string은 가변적인 상태를 유지하면서 불변 유형의 이점 중 일부를 누립니다.

문자열을 불변으로 만드는 것은 많은 이점이 있습니다.자동 스레드 안전을 제공하고 문자열이 간단하고 효과적인 방식으로 고유 유형처럼 동작하도록 합니다.또한 리소스 사용을 줄이기 위해 효과적인 문자열 인터닝을 허용하는 등 런타임에 추가적인 효율성을 제공하며 타사 API 호출이 문자열을 변경하는 것이 불가능하기 때문에 보안 이점이 큽니다.

StringBuilder는 불변 문자열의 한 가지 주요 단점을 해결하기 위해 추가되었습니다. 불변 유형의 런타임 구성은 많은 GC 압력을 발생시키고 본질적으로 느립니다.이를 처리하기 위해 명시적이고 가변적인 클래스를 만들면 문자열 클래스에 불필요한 복잡성을 추가하지 않고도 이 문제를 해결할 수 있습니다.

문자열은 실제로 불변하지 않습니다.그들은 단지 공개적으로 불변할 뿐입니다.즉, 공용 인터페이스에서 수정할 수 없습니다.하지만 그 안에는 실제로 변형이 가능합니다.

가 못, 저를보요.String.Concat리플렉터를 사용한 정의입니다.마지막 대사는...

int length = str0.Length;
string dest = FastAllocateString(length + str1.Length);
FillStringChecked(dest, 0, str0);
FillStringChecked(dest, length, str1);
return dest;

바와 FastAllocateString 빈문자 열을문 할은다 의수정 다됩니 해에음 으로 수정합니다.FillStringChecked

그 사은실.FastAllocateString이고 는외방이며법부,,FillStringChecked안전하지 않으므로 포인터를 사용하여 바이트를 복사합니다.

아마도 더 좋은 예들이 있을 수도 있지만 이것은 제가 지금까지 찾은 것입니다.

문자열 관리는 비용이 많이 드는 프로세스입니다.문자열을 불변으로 유지하면 반복된 문자열을 다시 만드는 대신 재사용할 수 있습니다.

C#에서 문자열 유형이 변경되지 않는 이유는 무엇입니까?

문자열은 참조 유형이므로 복사되지 않고 참조로 전달됩니다.이 값을 값으로 전달되는 C++ std::string 개체(변하지 않음)와 비교합니다.이것은 만약 당신이 해시 테이블에서 문자열을 키로 사용하고 싶다면, 당신은 C++에서 괜찮다는 것을 의미합니다. 왜냐하면 C++은 나중에 비교하기 위해 문자열을 복사하여 해시 테이블에 키를 저장하기 때문입니다(실제로 std:: hash_map, 하지만 여전히).따라서 나중에 std:: string 인스턴스를 수정해도 괜찮습니다.그런데.Net, 해시 테이블에서 문자열을 사용하면 해당 인스턴스에 대한 참조가 저장됩니다.이제 끈이 불변하지 않다고 가정하고, 무슨 일이 일어나는지 봅시다: 1.누군가가 "안녕하세요" 키가 있는 x 값을 해시 테이블에 삽입합니다. 2.해시 테이블은 문자열에 대한 해시 값을 계산하고 적절한 버킷에 문자열과 값 x를 참조합니다. 3.사용자가 String 인스턴스를 "bye"로 수정합니다. 4. 이제 누군가 "hello"와 연결된 해시 테이블의 값을 원합니다.그것은 결국 올바른 양동이를 찾지만, 문자열을 비교할 때 "안녕"이라고 말합니다!="안녕", 그래서 값은 반환되지 않습니다. 5. 누군가 "안녕" 값을 원하는 것일까요?"bye"는 아마도 다른 해시를 가지고 있기 때문에 해시 테이블은 다른 버킷에서 찾을 수 있습니다.그 양동이에 "안녕" 키가 없어서, 우리의 항목은 여전히 찾을 수 없습니다.

문자열을 불변으로 만든다는 것은 3단계가 불가능하다는 것을 의미합니다.만약 누군가가 문자열을 수정한다면, 그는 새로운 문자열 개체를 만들고, 이전 개체는 그대로 둡니다.즉, 해시 테이블의 키가 여전히 "안녕하세요"이므로 정확합니다.

따라서, 아마도 불변 문자열은 참조를 통해 전달된 문자열이 해시 테이블 또는 유사한 사전 객체의 키로 사용될 수 있도록 하는 방법일 것입니다.

이것을 덧붙이자면, 종종 잊혀지는 보안 보기입니다. 문자열이 변형 가능한 경우 이 시나리오를 상상해 보십시오.

string dir = "C:\SomePlainFolder";

//Kick off another thread
GetDirectoryContents(dir);

void GetDirectoryContents(string directory)
{
  if(HasAccess(directory) {
    //Here the other thread changed the string to "C:\AllYourPasswords\"
    return Contents(directory);
  }
  return null;
}

문자열이 전달되면 변형이 허용된다면 매우, 매우 나쁜 일이 될 수 있습니다.

변경 불가능한 데이터를 방어적으로 복사할 필요가 없습니다.변형을 위해 복사해야 하지만 종종 자유롭게 별칭을 지정할 수 있고 이러한 별칭의 의도하지 않은 결과에 대해 걱정할 필요가 없는 기능은 방어적 복사의 부족으로 인해 더 나은 성능으로 이어질 수 있습니다.

문자열은 에서 참조 유형으로 전달됩니다.그물.

참조 유형은 관리되는 힙에 상주하는 실제 인스턴스에 대한 포인터를 스택에 배치합니다.이는 스택에 전체 인스턴스를 보유하는 값 유형과는 다릅니다.

값 유형이 매개 변수로 전달되면 런타임은 스택에 값의 복사본을 만들고 해당 값을 메서드로 전달합니다.따라서 업데이트된 값을 반환하려면 정수를 'ref' 키워드와 함께 전달해야 합니다.

참조 유형이 전달되면 런타임은 포인터의 복사본을 스택에 만듭니다.복사된 포인터는 여전히 참조 유형의 원래 인스턴스를 가리킵니다.

문자열 유형에는 포인터 복사본 대신 자체 복사본을 만드는 오버로드된 = 연산자가 있으므로 값 유형과 더 유사하게 동작합니다.그러나 포인터만 복사한 경우 두 번째 문자열 작업이 실수로 다른 클래스의 개인 멤버 값을 덮어써 상당히 불쾌한 결과를 초래할 수 있습니다.

다른 게시물에서 언급했듯이 StringBuilder 클래스는 GC 오버헤드 없이 문자열을 만들 수 있습니다.

문자열 및 기타 구체적인 객체는 일반적으로 가독성 및 런타임 효율성을 향상시키기 위해 불변 객체로 표현됩니다.보안은 또 다른 문제입니다. 프로세스는 문자열을 변경하고 문자열에 코드를 주입할 수 없습니다.

가변 문자열을 함수에 전달하지만 변경될 것으로 예상하지 않는다고 가정합니다.그러면 함수가 그 문자열을 변경하면 어떻게 됩니까?예를 들어, C++에서는 단순히 값별로 호출할 수 있습니다.std::string그리고.std::string&파라미터), 그러나 C#에서는 모든 참조에 관한 것이므로 모든 함수에 가변 문자열을 전달하면 이를 변경하고 예상치 못한 부작용을 유발할 수 있습니다.

이것은 다양한 이유 중 하나일 뿐입니다.성능은 또 다른 것입니다(예: 인터내셔날 문자열).

클래스 데이터가 저장 클래스의 제어 범위 밖에서 수정할 수 없는 데이터를 저장하는 다섯 가지 일반적인 방법은 다음과 같습니다.

  1. 값 유형 원시 요소로 사용
  2. 관심 속성이 모두 불변인 클래스 개체에 대한 자유 공유 가능한 참조 보유
  3. 관심 있는 속성을 변형시킬 수 있는 어떤 것에도 절대 노출되지 않는 가변 클래스 개체에 대한 참조 보유
  4. 구조로서, "변종"이든 "불변종"이든 모든 필드가 #1-#4(#5가 아님) 유형입니다.
  5. 속성이 해당 참조를 통해서만 변환될 수 있는 개체에 대한 참조의 유일한 현존 복사본을 보유함.

문자열은 가변 길이이므로 값 형식의 원시 문자열일 수 없고 문자 데이터를 구조체에 저장할 수도 없습니다.나머지 선택 사항 중 문자열의 문자 데이터를 어떤 종류의 불변 객체에 저장할 필요가 없는 것은 #5뿐입니다.옵션 #5를 중심으로 프레임워크를 설계하는 것이 가능하지만, 그 선택은 제어 범위 밖에서 변경할 수 없는 문자열의 복사본을 원하는 모든 코드가 자체적으로 개인 복사본을 만들어야 합니다.그렇게 하는 것이 거의 불가능하지 않지만, 그렇게 하는 데 필요한 추가 코드의 양과 모든 것의 방어 복사본을 만드는 데 필요한 추가 런타임 처리의 양은 다음과 같은 이점을 가지고 있을 수 있습니다.string특히 변형 가능한 문자열 유형이 있다는 점을 고려할 때,System.Text.StringBuilder할 수 %를 하는 것입니다.string.

또한 불변 문자열은 동시성과 관련된 문제를 방지합니다.

당신의 등 뒤에서 다른 스레드가 수정하고 있는 문자열로 작업하는 OS라고 상상해 보십시오.사본을 만들지 않고 어떻게 검증할 수 있습니까?

언급URL : https://stackoverflow.com/questions/2365272/why-net-string-is-immutable