C言語では文字列の実態はchar型の配列だが、C#の場合は文字列型のオブジェクトである。
文字列型(string)は参照型である。
文字列は不変であり、一度作成した文字列は変更できない。string型変数の内容を変更する場合は毎回新しいインスタンスが作られる。
参照されなくなった文字列はガベージとなり、ガベージコレクションで回収される。
同じ文字列でも毎回インスタンスを生成すると、ガベージが増えてしまう。そのため、以前作られた文字列と同じ文字列である場合、インターンプールにより同一のインスタンスが利用される。(同じ参照になる)
インターンプールとは生成した文字列を格納しておく場所で、あとから文字列を生成する際、インターンプールに同じ文字列が存在していればそれを取得し、新たなインスタンスは生成しない。
インターンプールに登録されるかどうかは基準がある。例えば、リテラルは自動的に登録されるが、StringBuilderで生成した文字列は登録されない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//同じアセンブリ内の定数文字列は、常にランタイムによってインターンされる。 //(メモリ内の同じ場所に保存される=参照が等しくなる) var str1 = "あいうえお"; var str2 = "あいうえお"; Console.WriteLine(Object.ReferenceEquals(str1, str2)); //true //文字列クラスは==演算子をオーバーロードし、等価比較を実行する。 Console.WriteLine(str1 == str2); //true //新しい文字列がstr2に代入されるとインターンされなくなり、参照の同等性がなくなる。 str2 = "かきくけこ"; Console.WriteLine(Object.ReferenceEquals(str1, str2)); //false //再度str1と同じ文字列を代入するとインターンされ、参照が等しくなる。 str2 = "あいうえお"; Console.WriteLine(Object.ReferenceEquals(str1, str2)); //true //実行時に作成される文字列はインターンできない。 //そのためStringBuilderで生成した文字列はReferenceEqualsで同値比較できない。 StringBuilder sb = new StringBuilder("あいうえお"); string str3 = sb.ToString(); Console.WriteLine(Object.ReferenceEquals(str1, str3)); //false Console.WriteLine(str1 == str3); //true |
明示的にインターンプールに登録する
String.Internを使用すると、明示的にインターンプールに登録することができる。既に登録されている場合は何もしない。
1 2 3 4 5 6 |
string str4 = "あいうえお"; string.Intern(str4); string str5 = "かきくけこ"; string str6 = string.Intern(str5); //登録された文字列を取得することができる Console.WriteLine(str6); //かきくけこ |
参考サイト: 2019-12-16 (C#) string で発生するガベージを抑える (String Interning)2/16/195847
コメント