1. ホーム
  2. c#

[解決済み] .NETのStringはなぜimmutableなのか?[重複あり]

2022-04-19 22:51:28

質問

周知のとおりです。 文字列 はイミュータブルです。Stringがimmutableである理由、そして ストリングビルダー クラスはミュータブルですか?

どのように解決するのですか?

  1. 不変型のインスタンスは本質的にスレッドセーフです。どのスレッドもそれを変更できないので、あるスレッドが他のスレッドと干渉する形でそれを変更するリスクは取り除かれます(参照自体は別の問題です)。
  2. 同様に、エイリアシングでは変更ができないため(xとyの両方が同じオブジェクトを参照している場合、xの変更はyの変更を伴う)、コンパイラの最適化がかなり可能になっています。
  3. また、メモリを節約する最適化も可能です。インターナルやアトマイジングが最もわかりやすい例ですが、同じ原理の他のバージョンも可能です。私は以前、不変オブジェクトを比較し、重複する参照を置き換えて、すべて同じインスタンスを指すようにすることで、約半GBのメモリ節約を実現しました(時間はかかりますが、大量のメモリを節約するための1分間の追加起動は、問題のケースにおいてパフォーマンスの勝利となりました)。ミュータブルオブジェクトの場合、それはできません。
  4. でない限り、メソッドとして不変の型をパラメータに渡しても、副作用は生じない。 out または ref (これはオブジェクトではなく参照を変更するため)。したがって、プログラマは、もし string x = "abc" をメソッドの最初に書いて、それがメソッド本体で変化しないのであれば x == "abc" をメソッド終了時に指定します。
  5. 概念的には、セマンティクスはより値型に近いものです。特に、等価性は同一性ではなく、状態に基づいています。これは、以下のことを意味します。 "abc" == "ab" + "c" . これは不変性を必要としませんが、このような文字列への参照はその寿命を通じて常に "abc" と等しいという事実(これは不変性を必要とします)により、以前の値との等質性が不可欠なキーとしての使用は、より簡単に正確性を確保できます(実際、キーとしてよく使用される文字列です)。
  6. 概念的には、イミュータブルである方が理にかなっている場合もあります。クリスマスに1ヶ月追加しても、クリスマスを変えたわけではなく、1月下旬に新しい日付を生成したことになります。したがって、次のようにするのが理にかなっています。 Christmas.AddMonths(1) は新しい DateTime を変更するのではなく、ミュータブルなものを変更するのです。(他の例として、もし私がmutableなオブジェクトとして自分の名前を変更した場合、変更されたのは私が使っている名前であり、quot;Jon"は不変のままで、他のJonは影響を受けません。
  7. コピーは高速かつシンプルで、クローンを作成するには、単に return this . コピーはどうせ変更できないので、何かを自分のコピーであるかのように装うことは安全です。
  8. [編集部、これ忘れてました]。内部状態をオブジェクト間で安全に共有することができる。例えば、配列と開始インデックスとカウントでバックされるリストを実装していた場合、サブレンジを作るのに最もコストがかかるのは、オブジェクトのコピーでしょう。しかし、もしそれが不変であれば、サブレンジオブジェクトは同じ配列を参照し、開始インデックスとカウントだけが変更され、その結果、サブレンジオブジェクトは 非常に 構築時間が大幅に短縮されます。

全体として、その目的の一部として変化する必要のないオブジェクトの場合、イミュータブルであることには多くの利点があると言えるでしょう。主な欠点は余分なコンストラクタを必要とすることですが、ここでもしばしば誇張されています(StringBuilderが同等のコンストラクタを持つ一連の連結よりも効率的になるまでに何度も追加を行わなければならないことを思い出してください)。

もし、ミュータビリティがオブジェクトの目的の一部であるならば、それは不利になるでしょうが(給料が決して変わらないEmployeeオブジェクトによってモデル化されたい人はいないでしょう)、時にはそれが役に立つこともあります(多くのWebや他のステートレスアプリケーションでは、読み取り操作を行うコードは更新を行うコードとは別で、異なるオブジェクトを使うことが自然でしょう - 私はオブジェクトを不変にしてそのパターンを強制しませんが、もし私がすでにそのパターンを持っていたら、パフォーマンスと正確性の保証のために私の "read" オブジェクトを不変にしてしまうでしょう)。

Copy-on-writeは中間的な存在です。ここでは、"real" クラスは、"state" クラスへの参照を保持しています。ステートクラスはコピー操作で共有されますが、ステートを変更すると、ステートクラスの新しいコピーが作成されます。これはC#よりもC++でよく使われます。そのため、std:stringはmutableでありながら、immutable型の利点を一部(全てではありませんが)享受しています。