string型は参照型
string型はプリミティブ型に分類されますが、参照型です。よく値型と勘違いされますが誰がなんと言おうと参照型です。
でも、何も考えず以下のようなコードを書くとどうでしょう。結果としては値型のような挙動になります。
string str1 = "Hoge"; string str2 = str1; str2 = "Piyo"; Console.WriteLine(str1); Console.WriteLine(str2);
Dim str1 As String = "Hoge" Dim str2 As String = str1 str2 = "Piyo" Console.WriteLine(str1) Console.WriteLine(str2)
参照型ならstr2はnewしていないので、変更を加えると同じ参照先を保持しているstr1にも影響が出て、どちらも「Piyo」になる・・・はずですが、結果は以下になります。
Hoge Piyo
string型はよく使う型の一つですが、例の通り値型のような動きをするので勘違いしやすいです。
例えば、Hogeをfor文などで何度も+=(&=)演算子で追加するようなプログラムを考えます。以下のようなイメージを持っていませんか。
しかしこれは間違いです。
実際は以下のようなイメージです。
string型は参照型ではありますが、少し特殊でnewをしなくても変更が加わった瞬間に勝手にnewする型なのです。
つまり、変更がかかる度に内部的に別のインスタンスを作成して新しい箱に値を保持してしまいます。よって最初の例の2行目「string str2 = str1;」という書き方をしても、str2とstr1は値型と同じような「全く別物」として扱われます。
ここで問題になるのが、長文を+=(&=)演算子で追加するような場合です。
これは非常にコストがかかりますので、本来はSringBuilderを利用しなければなりませんが、果たしてどれくらい違うのでしょうか。
速度を測る方法もありますが、今回はメモリ使用量を見てみましょう。以下のテストコードを自分で実行してみて下さい。
string str = System.String.Concat(Enumerable.Repeat("hogehogehogehogehoge", 1000).ToArray()); Console.WriteLine(Environment.WorkingSet); //string型 string str1 = string.Empty; for (int i = 1; i <= 1000; i++) { str1 += "hogehogehogehogehoge"; } Console.WriteLine(Environment.WorkingSet); //StringBuilder System.Text.StringBuilder str2 = new System.Text.StringBuilder(); for (int i = 1; i <= 1000; i++) { str2.Append("hogehogehogehogehoge"); } Console.WriteLine(Environment.WorkingSet);
Dim str As String = System.String.Concat(Enumerable.Repeat("hogehogehogehogehoge", 1000).ToArray()) Console.WriteLine(Environment.WorkingSet) 'String型 Dim str1 As String = String.Empty For i As Integer = 1 To 1000 str1 &= "hogehogehogehogehoge" Next Console.WriteLine(Environment.WorkingSet) 'StringBuilder Dim str2 As New System.Text.StringBuilder For i As Integer = 1 To 1000 str2.Append("hogehogehogehogehoge") Next Console.WriteLine(Environment.WorkingSet)
結果はどうなりましたか?