C#の可変文字列と不変文字列の違いは何ですか?


190

C#の可変文字列と不変文字列の違いは何ですか?



6
同じではありません。非常に似ていますが、同一とは異なります。
Marcelo Cantos、2010年

2
StringBuilderの内部についての関連する質問stackoverflow.com/q/3564906/38206
Brian Rasmussen '25

回答:


313

ミュータブルとイミュータブルは、それぞれ「変更可能」と「変更不可」を意味する英語の単語です。単語の意味はITのコンテキストでも同じです。すなわち

  • 変更可能な文字列は変更できます。
  • 不変文字列は変更できません。

これらの単語の意味は、C#/ .NETでも他のプログラミング言語/環境と同じですが、(明らかに)タイプの名前が他の詳細と異なる場合があります。


記録のために:

  • String 標準のC#/ .Net不変文字列型です
  • StringBuilder 標準のC#/ .Net可変文字列型

C#として表される文字列に「変更を加える」には、String実際に新しいStringオブジェクトを作成します。オリジナルStringは変更できないので変更されません...

ほとんどの場合String、それらについてのより簡単な理由なので、使用する方が良いです。例えば、他のスレッドが「私の文字列を変更する」可能性を考慮する必要はありません。ただし、一連の操作を使用して文字列を作成または変更する必要がある場合は、を使用する方が効率的StringBuilderです。


そして最後に、a StringBuilderは不変ではないため文字列ではないと断言する人々のために、MicrosoftのドキュメントStringBuilderこう記述しています:

変更可能な文字列表します。このクラスは継承できません。」


182

String 不変です

つまり、文字列は変更できません。文字列を変更する(たとえば、文字列に追加する)と、実際には新しい文字列が作成されます。

だが StringBuilder、不変ではありません(むしろ、変更可能です)

したがって、複数の連結など、文字列を何度も変更する必要がある場合は、を使用しますStringBuilder


4
不変文字列を何度も連結するとどうなりますか?参照のない文字列のメモリ使用量の負荷?または単に遅いですか?
Rudie

13
@Rudie-両方。連結ごとに新しいStringオブジェクトが作成され、両方の入力文字列のすべての文字がコピーされます。O(N^2)行動につながる...
スティーブンC

@StephenCがキーワードで説明しました。
ケントリアウ2015

6
複数の連結など、文字列を何度も変更する必要がある場合は、StringBuilderを使用します。これで私の心が一掃されました...
katmanco

45

オブジェクトは、一度作成されると、そのオブジェクトに対するさまざまな操作を呼び出すことによって状態を変更できる場合は変更可能です。それ以外の場合は変更できません。

不変文字列

C#(および.NET)では、文字列はSystem.Stringクラスで表されます。stringキーワードは、このクラスのエイリアスです。

System.Stringクラスは、すなわち一度その状態を変更することはできません作成され不変です

だからあなたのような文字列に対して実行するすべての操作はSubstringRemoveReplace、などの「+」演算子を使用して連結は、新しい文字列を作成し、それを返します。

デモについては、次のプログラムを参照してください-

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

これにより、それぞれ「string」と「mystring」が出力されます。

不変性利点と文字列が不変である理由の確認.NET文字列が不変である理由

可変文字列

頻繁に変更する文字列が必要な場合は、StringBuilderクラスを使用できます。インスタンスに対する操作は StringBuilder 、同じオブジェクトを変更します

詳細なアドバイスのために使用する際 StringBuilderに参照するのStringBuilderを使用する場合は?


美しい説明!
Hari

36

すべてのstringオブジェクトはC#では不変です。クラスのオブジェクトはstring、いったん作成されると、作成時に使用したもの以外の値を表すことはできません。文字列を「変更」するように見えるすべての操作は、代わりに新しい文字列を生成します。これはメモリに関しては非効率的ですが、参照が変更されない限り、参照されている文字列は変更されないため、文字列があなたのもとで形式が変更されないことを信頼できるという点で非常に役立ちます。

対照的に、可変オブジェクトには変更可能なデータフィールドがあります。そのメソッドの1つまたは複数がオブジェクトの内容を変更するか、書き込み時にオブジェクトの値を変更するプロパティを持っています。

変更可能なオブジェクト(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
Ummm ...逆アセンブルされたコードは、ポインターの割り当てが行われ、メソッドが呼び出されていることのみを示しています。変更可能か不変かとは(ほとんど)関係ありません。(無関係なサンプルコードを逆アセンブルすることでこの種のことを理解するのに役立つと考える人がいるのはなぜかと思います。まず、ほとんどのプログラマーは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が文字列の参照を取得します。しかし、名前のような他の変数がまだそれを使用しているため、このプロセス全体「私の国」は変更されませんでした。そして、それがstringがIMMUTABLEである理由です

StringBuilderはさまざまな方法で機能し、文字列プールを使用しません。StringBuilderのインスタンスを作成するとき:

var address  = new StringBuilder(500);

このインスタンスにサイズ500バイトのメモリチャンクを割り当て、すべての操作でこのメモリの場所を変更するだけで、このメモリは他のオブジェクトと共有されません。そして、それがStringBuilderMUTABLEである理由です

お役に立てば幸いです。


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."

これは、文字列リテラルがデスクトップ.N​​ET Frameworkにインターンされるためです。つまり、まったく同じ文字列barbaz指すので、一方を変更するともう一方も変更されます。WinRTのような管理されていないプラットフォームを使用している場合、文字列のインターニングが欠けているので、これはすべて問題ありません。


1
はい、そうです。閉じられているはずの開いている抽象化をクラックしてルールを破った場合、ほとんど何も不変ではありません。厄介なリフレクションを使用してJavaで同様のことを行うことができますが、JLSは取得した動作が定義されていないと述べています。
スティーブンC

2

明確にするために、C#(または一般に.NET)には変更可能な文字列などはありません。他の言語は変更可能な文字列(変更可能な文字列)をサポートしていますが、.NETフレームワークはサポートしていません。

したがって、あなたの質問に対する正しい答えは、C#ではすべての文字列が不変であることです。

文字列には特定の意味があります。"string"の小文字のキーワードは、System.Stringクラスからインスタンス化されたオブジェクトのショートカットにすぎません。文字列クラスから作成されたすべてのオブジェクトは常に不変です。

テキストの変更可能な表現が必要な場合は、StringBuilderなどの別のクラスを使用する必要があります。StringBuilderを使用すると、「単語」のコレクションを繰り返し作成してから、それを文字列に変換できます(もう一度不変です)。


申し訳ありませんが、MicrosoftのドキュメントでStringBuilderはこれと矛盾します。msdn.microsoft.com
Stephen C

1

データ値は変更できません。注:変数値は変更される可能性がありますが、元の不変データ値は破棄され、新しいデータ値がメモリに作成されました。


1

実装の詳細。

CLR2のSystem.Stringは変更可能です。StringBuilder.Append呼び出しString.AppendInplace(プライベートメソッド)

CLR4のSystem.Stringは不変です。StringBuilderには、チャンク付きのChar配列があります。


0

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

短い答え:StringBuilderは変更可能ですが、Stringは不変です。

どういう意味ですか ?Wikiによると、オブジェクト指向では、不変オブジェクトとは、作成後に状態を変更できないオブジェクトのことです。これは、作成後に変更できる可変オブジェクトとは対照的です。

StringBuilderクラスのドキュメントから:

Stringオブジェクトは不変です。System.Stringクラスのメソッドの1つを使用するたびに、メモリ内に新しい文字列オブジェクトを作成します。これには、その新しいオブジェクトに新しい領域を割り当てる必要があります。

文字列に繰り返し変更を加える必要がある状況では、新しいStringオブジェクトの作成に関連するオーバーヘッドが高くつく可能性があります。

System.Text.StringBuilderクラスは、新しいオブジェクトを作成せずに文字列を変更する場合に使用できます。たとえば、StringBuilderクラスを使用すると、ループ内で多数の文字列を連結するときにパフォーマンスを向上させることができます。


0

これは、不変文字列と可変文字列ビルダーの例です

        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と同様に出力を提供できれば非常に良いでしょう:)
ロイ・リー

部分文字列は基になる値を変更しません。値を返すだけです。
Kye

@キーとその理由は?文字列は不変であるため:)
LuckyLikey

2行のコード-:s.Substring(0、5); Console.WriteLine(s); ここでs.substring()はsを変更しません。この例では、文字列が不変であるかどうかを示していません。
Dhananjay

0

C#の文字列は不変です。それを任意の文字列と連結すると、実際には新しい文字列、つまり新しい文字列オブジェクトが作成されます。しかし、StringBuilderは変更可能な文字列を作成します。


0

StringBuilderは可変文字列型であり、StringBuilderオブジェクトは不変型であるため、StringBuilderは巨大なデータ文字列を連結するための優れたオプションです。

連結のためにStringBuilderではなくstringを使用している場合、毎回メモリ内に新しいインスタンスが作成されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.