WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

文字列連結とインターンプール

by WebSurfer 2018年2月26日 15:37

文字列を複数に分けて連結する場合、インターンプールとの関係で注意した方がよさそうと思ったことを書きます。(元の話は MSDN Forum のスレッド「ASP.NET MVC で SQL文の塊を静的クラスで宣言することについて」です)

長い文字列を一行で書くと可読性が落ちるので、複数の行に分けて書くいうことがあると思います。

例えば、以下のコードのように、SQL Server に発行するクエリを複数の文字列に分けて、改行して、+ 演算子で連結するというケースを考えてみます。

その際、以下のコードの s2, s3 のようにするのは止めた方がよさそうです。その理由を以下に書きます。

string s0 = 
    "SELECT aaa, bbb, ccc FROM Table1 WHERE ddd = @ddd";

string s1 = "SELECT aaa, bbb, ccc " +
            "FROM Table1 " +
            "WHERE ddd = @ddd";

string s2 = "SELECT aaa, bbb, ccc ";
      s2 += "FROM Table1 ";
      s2 += "WHERE eee = @eee";

// StringBuilder で連結した結果はインターンされない。
// ただし Asspnd の引数の個々の文字列はインターンされる
string s3 = new StringBuilder().
                Append("SELECT aaa, bbb, ccc ").
                Append("FROM Table1 ").
                Append("WHERE fff = @fff").
                ToString();

// t1 は "WHERE ddd = @ddd"
string t1 = new StringBuilder().
                Append("WHERE ddd "). Append("= @ddd").
                ToString();

// t2 は "WHERE eee = @eee"
string t2 = new StringBuilder().
                Append("WHERE eee ").Append("= @eee").
                ToString();

// t3 は "WHERE fff = @fff"
string t3 = new StringBuilder().
                Append("WHERE fff ").Append("= @fff").
                ToString();

Console.WriteLine("s0 と s1 は同一オブジェクトを指す: {0}", 
                (object)s1 == (object)s0);

Console.WriteLine("t1 はインターンされている: {0}", 
                (string.IsInterned(t1) == null));

Console.WriteLine("t2 はインターンされている: {0}", 
                (string.IsInterned(t2) == null));

Console.WriteLine("t3 はインターンされている: {0}", 
                (string.IsInterned(t3) == null));


// 結果は:
// s0 と s1 は同一オブジェクトを指す: True
// t1 はインターンされている: True
// t2 はインターンされている: False
// t3 はインターンされている: False

s0 のコードで文字列 "SELECT aaa, bbb, ccc FROM Table1 WHERE ddd = @ddd" はインターンプールに格納さます。

s1 のコードでは、"SELECT aaa, bbb, ccc ", "FROM Table1 ", "WHERE ddd = @ddd" という複数の文字列に分けて、それらを + 演算子で連結しています。しかしながら、個々の文字列がインターンされることはなく、コンパイラは連結後の結果だけを見て、s0 のコードで生成されインターンされた文字列の参照を s1 に代入するようです。

s0 と s1 は同じオブジェクトを指すこと(即ち、(object)s1 == (object)s0 は true になります)と、StringBuilder で生成した t1("WHERE ddd = @ddd" という文字列)がインターンされてないことがそれを裏付けていると思います。

しかし、s2, s3 の場合、t2, t3 がインターンされているという結果からみると、右辺の個々の文字列のオブジェクトも作られ、それらがインターンプールに格納されるという無駄なことが行われるようです。

ちなみに、インターンプールというのは、文字列を key に、ヒープ上の String オブジェクトのアドレスを value に持つハッシュテーブルのようなものだそうです。詳しくは MSDN ライブラリの記事「String.Intern メソッド」の記事の解説を見てください。

寿命についてもインターンプール特有のものがあるそうです。詳しくは上の記事の「パフォーマンスに関する考慮事項」のセクションを見てください。

Tags:

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年5月  >>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar