C #에서 변경 가능한 문자열과 변경 불가능한 문자열의 차이점은 무엇입니까?


190

C #에서 변경 가능한 문자열과 변경 불가능한 문자열의 차이점은 무엇입니까?



6
동일하지 않습니다. 매우 유사하지만 동일하지 않습니다.
Marcelo Cantos

2
모두 StringBuilder의 내부에 대한 관련 질문 stackoverflow.com/q/3564906/38206
브라이언 라스무센

답변:


313

가변 및 불변은 각각 "변경 가능"및 "변경 불가능"을 의미하는 영어 단어입니다. 단어의 의미는 IT 컨텍스트에서 동일합니다. 즉

  • 변경 가능한 문자열을 변경할 수 있으며
  • 변경할 수없는 문자열은 변경할 수 없습니다.

이 단어의 의미는 C # / .NET에서 다른 프로그래밍 언어 / 환경에서와 동일하지만 다른 세부 사항과 마찬가지로 유형의 이름은 (명백하게) 다를 수 있습니다.


기록을 위해 :

  • String 표준 C # / .Net 불변 문자열 유형입니다.
  • StringBuilder 표준 C # / .Net 가변 문자열 유형입니다.

C #으로 표시되는 문자열에서 "변경"에 영향을 주려면 String실제로 새 String객체를 만듭니다. 원래String 은 변경할 수 없기 때문에 변경되지 않습니다.

대부분의 경우 더 String쉬운 이유이기 때문에 사용하는 것이 좋습니다 . 예를 들어, 다른 스레드가 "내 문자열을 변경할"가능성을 고려할 필요가 없습니다. 그러나 일련의 작업을 사용하여 문자열을 구성하거나 수정해야하는 경우을 사용하는 것이 더 효율적일 수 있습니다 StringBuilder.


마지막으로, a StringBuilder는 변경할 수 없기 때문에 a가 문자열이 아니라고 주장하는 사람들을 위해 Microsoft 설명서 는 다음과 StringBuilder같이 설명합니다 .

" 변경 가능한 문자열 을 나타냅니다.이 클래스는 상속 될 수 없습니다."


182

String 불변이다

즉, 문자열을 변경할 수 없습니다. 문자열을 변경하면 (예 : 문자열을 추가하여) 실제로 새 문자열을 만듭니다.

그러나 StringBuilder 불변은 아닙니다 (대신 변할 수 있습니다)

따라서 여러 연결과 같이 문자열을 여러 번 변경해야하는 경우을 사용하십시오 StringBuilder.


4
불변 문자열을 여러 번 연결하면 어떻게됩니까? 참조가없는 문자열에 대한 메모리 사용량이 있습니까? 아니면 천천히?
Rudie

13
@ 루디-둘 다. 각 연결은 새 String 객체를 만들고 두 입력 문자열의 모든 문자를 복사합니다. 리드 O(N^2)행동 ...
스티븐 C

@StephenC는 키워드로 설명했습니다.
Kent Liau

6
여러 연결과 같이 문자열을 여러 번 변경해야하는 경우 StringBuilder를 사용하십시오. 덕분에 마음이 완전히 사라
졌습니다

45

한 번 작성된 오브젝트는 다양한 조작을 호출하여 상태를 변경할 수 있으면 변경할 수 있으며, 그렇지 않으면 변경할 수 없습니다.

불변 문자열

C # (및 .NET)에서 문자열은 System.String 클래스로 표시됩니다. string키워드는이 클래스의 별칭입니다.

선택 System.String 클래스는 불변, 즉 한 번 그 상태를 변경할 수 없습니다 만든 .

모든 작업 그래서 당신은 같은 문자열에서 수행 Substring, Remove, Replace, 연결 사용 '+'연산자 등 새로운 문자열을 만들고 반환합니다.

데모는 다음 프로그램을 참조하십시오-

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);

이것은 각각 'string'과 'mystring'을 인쇄합니다.

불변성이점 과 왜 문자열을 변경할 수 없는지 확인하려면 왜 .NET 문자열을 변경할 수 없습니까? .

가변 문자열

자주 수정하려는 문자열을 원한다면 StringBuilder클래스를 사용할 수 있습니다 . 인스턴스 에 대한 작업 StringBuilder 은 동일한 객체를 수정합니다 .

사용시기 에 대한 자세한 내용 은 StringBuilder 사용시기를StringBuilder 참조하십시오 . .


아름다운 설명!
Hari

36

stringC #에서는 모든 개체를 변경할 수 없습니다. string일단 생성 된 클래스의 객체는 자신이 생성 한 것 이외의 다른 값을 나타낼 수 없습니다. 문자열을 "변경"하는 것처럼 보이는 모든 작업은 새로운 작업을 생성합니다. 이것은 메모리에는 비효율적이지만 참조가 변경되지 않는 한 참조되는 문자열은 절대 변경되지 않기 때문에 문자열이 사용자의 형식에서 변경되지 않는다는 것을 신뢰할 수 있다는 점에서 매우 유용합니다.

반대로 변경 가능한 개체에는 변경할 수있는 데이터 필드가 있습니다. 하나 이상의 메소드가 오브젝트의 컨텐츠를 변경하거나, 작성 될 때 오브젝트의 값을 변경하는 특성을 갖습니다.

String과 가장 유사한 가변 객체가있는 StringBuffer경우 아래에서 변경되지 않도록 절대로 복사하려면 사본을 만들어야합니다. 그렇기 때문에 가변 객체가 모든 형태의 키로 사용하기에 위험한 이유Dictionary 나 자신을 변화시킬 수있는 개체를 해주는데 따른 손쉬운 셋업, 데이터 구조는 결국 프로그램을 충돌 할 데이터가 손상으로 이어지는, 알 방법이 없습니다.

그러나 내용을 변경할 수 있으므로 단일 문자 또는 이와 유사한 문자를 변경하고 싶기 때문에 완전한 사본을 만드는 것보다 훨씬 더 메모리 효율적입니다.

일반적으로 올바른 작업은 무언가를 만드는 동안 가변 객체를 사용하고 일단 완료하면 불변 객체를 사용하는 것입니다. 이것은 물론 불변의 형태를 가진 객체에 적용됩니다. 대부분의 컬렉션은 그렇지 않습니다. 컬렉션의 내부 상태를 다른 컨텍스트로 보낼 때 변경 불가능한 읽기 전용 형식의 컬렉션을 제공하는 것이 종종 유용합니다. 데이터.


12

불변 :

객체에서 일부 작업을 수행하면 새 객체가 생성되므로 문자열의 경우와 같이 상태를 수정할 수 없습니다.

변하기 쉬운

객체에 대한 작업을 수행하면 StringBuilder의 경우와 같이 객체 자체가 수정되지 않은 새로운 객체가 수정되었습니다.


8

.NET에서 System.String (일명 문자열)은 변경할 수없는 개체입니다. 즉, 객체를 만들 때 나중에 값을 변경할 수 없습니다. 변경할 수없는 객체 만 다시 만들 수 있습니다.

System.Text.StringBuilder는 System.String과 동일하게 변경 가능하며 값을 변경할 수 있습니다.

예를 들어 :

class Program
{
    static void Main(string[] args)
    {

        System.String str = "inital value";
        str = "\nsecond value";
        str = "\nthird value";

        StringBuilder sb = new StringBuilder();
        sb.Append("initial value");
        sb.AppendLine("second value");
        sb.AppendLine("third value");
    }
}

다음 MSIL을 생성합니다. 코드를 조사한 경우. System.String의 객체를 변경할 때마다 실제로 새로운 객체를 생성하고 있음을 알 수 있습니다. 그러나 System.Text.StringBuilder에서는 텍스트 값을 변경할 때마다 개체를 다시 만들지 않습니다.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  2
  .locals init ([0] string str,
           [1] class [mscorlib]System.Text.StringBuilder sb)
  IL_0000:  nop
  IL_0001:  ldstr      "inital value"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "\nsecond value"
  IL_000c:  stloc.0
  IL_000d:  ldstr      "\nthird value"
  IL_0012:  stloc.0
  IL_0013:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0018:  stloc.1
  IL_0019:  ldloc.1
  IL_001a:  ldstr      "initial value"
  IL_001f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  ldstr      "second value"
  IL_002b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_0030:  pop
  IL_0031:  ldloc.1
  IL_0032:  ldstr      "third value"
  IL_0037:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_003c:  pop
  IL_003d:  ret
} // end of method Program::Main

5
음 ... 분해 된 코드는 포인터 할당이 이루어지고 메소드가 호출되고 있음을 보여줍니다. 변경 가능한 것과 변경 불가능한 것과는 거의 관련이 없습니다. (이것은 일부 사람들이 관련이없는 예제 코드를 분해하면 사람들이 이런 종류의 것을 이해하는 데 도움이 될 것이라고 생각하는 이유를 피합니다. 시작하기 위해 대부분의 프로그래머는 CLI 코드에 유창하지 않습니다.)
Stephen C

6

.NET은이면에서 문자열 풀을 사용하므로 문자열을 변경할 수 있습니다. 그 뜻은 :

string name = "My Country";
string name2 = "My Country";

name과 name2는 모두 문자열 풀에서 동일한 메모리 위치를 참조합니다. 이제 name2를 다음과 같이 변경한다고 가정하십시오.

name2 = "My Loving Country";

문자열 "My Loving Country"에 대한 문자열 풀을 살펴보면 참조를 얻을 수 있습니다. 현명한 새 문자열 "My Loving Country"가 문자열 풀에 만들어지고 name2가 참조를 얻습니다. 그러나 이름 과 같은 다른 변수 가 여전히 사용하고 있기 때문에이 전체 프로세스 " 내 국가 "는 변경되지 않았습니다 . 이것이 문자열이 IMMUTABLE 인 이유 입니다.

StringBuilder 는 다른 방식으로 작동하며 문자열 풀을 사용하지 않습니다. StringBuilder 인스턴스를 만들 때 :

var address  = new StringBuilder(500);

이 인스턴스에 대해 500 바이트 크기의 메모리 청크를 할당하고 모든 조작은이 메모리 위치를 수정하며이 메모리는 다른 오브젝트와 공유되지 않습니다. 이것이 StringBuilder변경 가능한 이유 입니다 . 입니다.

도움이 되길 바랍니다.


5

실제로는 없습니다. String 클래스는 변경 가능합니다.

unsafe
{
    string foo = string.Copy("I am immutable.");
    fixed (char* pChar = foo)
    {
        char* pFoo = pChar;

        pFoo[5] = ' ';
        pFoo[6] = ' ';
    }

    Console.WriteLine(foo); // "I am   mutable."
}

이러한 종류의 논리는 실제로 String 및 StringBuilder 클래스에서 항상 수행됩니다. Concat, Substring 등을 호출 할 때마다 새 문자열을 할당하고 포인터 산술을 사용하여 새 문자열로 복사합니다. 문자열은 스스로 변이되지 않으므로 "불변"으로 간주되는 이유는 무엇입니까?


그건 그렇고, 문자열 리터럴로 이것을 시도 하지 마십시오. 그렇지 않으면 프로그램이 심하게 엉망이됩니다.

string bar = "I am a string.";

fixed (char* pChar = bar)
{
    char* pBar = pChar;

    pBar[2] = ' ';
}

string baz = "I am a string.";

Console.WriteLine(baz); // "I  m a string."

이는 문자열 리터럴이 데스크톱 .NET Framework에 포함되어 있기 때문입니다. 즉,에서 barbaz동일한 문자열을 가리키고, 그래서 하나가 다른 변이 것이다 돌연변이. WinRT와 같은 관리되지 않는 플랫폼을 사용하는 경우 문자열 인턴이 부족하지만 이것은 훌륭하고 멋집니다.


1
그래 닫혀 야하는 열린 추상화를 해독하여 규칙을 어기면 거의 변하지 않습니다. Java에서 리플렉션을 사용하여 유사한 작업을 수행 할 수 있지만 JLS는 사용자가 얻는 동작이 정의되어 있지 않다고 말합니다.
Stephen C

2

명확히하기 위해 C # (또는 일반적으로 .NET)에는 가변 문자열과 같은 것이 없습니다. 다른 언어는 변경 가능한 문자열 (변경 가능한 문자열)을 지원하지만 .NET 프레임 워크는 지원하지 않습니다.

따라서 귀하의 질문에 대한 정답은 C #에서 모든 문자열을 변경할 수 없다는 것입니다.

문자열에는 특정한 의미가 있습니다. "문자열"소문자 키워드는 System.String 클래스에서 인스턴스화 된 객체의 바로 가기입니다. 문자열 클래스에서 생성 된 모든 객체는 항상 변경할 수 없습니다.

변경 가능한 텍스트 표현을 원하면 StringBuilder와 같은 다른 클래스를 사용해야합니다. StringBuilder를 사용하면 반복적으로 '단어'모음을 만든 다음이를 문자열로 변환 할 수 있습니다 (다시 한번 불변).


죄송하지만 Microsoft 설명서는 이에 StringBuilder반합니다. msdn.microsoft.com/en-us/library/…
Stephen C

1

데이터 값이 변경되지 않을 수 있습니다. 참고 : 변수 값이 변경 될 수 있지만 원래의 변경 불가능한 데이터 값이 삭제되고 메모리에 새 데이터 값이 작성되었습니다.


1

구현 세부 사항.

CLR2의 System.String은 변경 가능합니다. String.AppendInplace를 호출하는 StringBuilder.Append (비공개 메소드)

CLR4의 System.String은 변경할 수 없습니다. StringBuilder에는 청킹이 포함 된 Char 배열이 있습니다.


0

에서 http://yassershaikh.com/what-is-the-difference-between-strings-and-stringbuilder-in-c-net/

짧은 답변 : String은 변경할 수 없지만 StringBuilder는 변경할 수 있습니다.

그게 무슨 뜻이야? Wiki 말한다 : 객체 지향에서, 불변 객체는 생성 된 후에 상태를 수정할 수없는 객체입니다. 이는 변경 가능한 객체와 달리 생성 된 후에 수정할 수 있습니다.

StringBuilder 클래스 문서에서 :

String 객체는 변경할 수 없습니다. System.String 클래스의 메서드 중 하나를 사용할 때마다 메모리에 새 문자열 개체가 만들어 지므로 해당 새 개체에 대한 새로운 공간 할당이 필요합니다.

문자열을 반복해서 수정해야하는 상황에서는 새 문자열 객체를 만드는 데 따르는 오버 헤드가 많이 발생할 수 있습니다.

새 개체를 만들지 않고 문자열을 수정하려는 경우 System.Text.StringBuilder 클래스를 사용할 수 있습니다. 예를 들어, StringBuilder 클래스를 사용하면 루프에서 많은 문자열을 함께 연결할 때 성능이 향상 될 수 있습니다.


0

다음은 Immutable string 및 Mutable string builder의 예입니다.

        Console.WriteLine("Mutable String Builder");
        Console.WriteLine("....................................");
        Console.WriteLine();
        StringBuilder sb = new StringBuilder("Very Good Morning");
        Console.WriteLine(sb.ToString());
        sb.Remove(0, 5);
        Console.WriteLine(sb.ToString());

        Console.WriteLine();

        Console.WriteLine("Immutable String");
        Console.WriteLine("....................................");
        Console.WriteLine();
        string s = "Very Good Morning";
        Console.WriteLine(s);
        s.Substring(0, 5);
        Console.WriteLine(s);
        Console.ReadLine();

@Gokul :) 출력도 제공 할 수 있다면 매우 좋을 것입니다.
Roy Lee

부분 문자열은 기본 값을 변경하지 않습니다. 값만 반환합니다.
Kye

@Kye와 왜? 문자열은 변경할 수 없기 때문에 :)
LuckyLikey

두 줄 코드-: s.Substring (0, 5); Console.WriteLine (s); 여기서 s.substring ()은 s를 변경하지 않습니다. 이 예제는 문자열이 변경 불가능한지를 표시하지 않습니다.
다난 자이

0

C #의 문자열은 변경할 수 없습니다. 문자열과 연결하면 실제로 새 문자열, 즉 새 문자열 객체를 만드는 것입니다! 그러나 StringBuilder는 가변 문자열을 만듭니다.


0

StringBuilder는 가변 문자열 유형이고 StringBuilder 객체는 불변 유형이므로 StringBuilder는 큰 데이터 문자열을 연결하는 더 나은 옵션입니다. 즉, StringBuilder는 문자열을 연결하는 동안 객체의 새 인스턴스를 만들지 않습니다.

연결을 위해 StringBuilder 대신 문자열을 사용하는 경우 매번 메모리에 새 인스턴스가 생성됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.