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 で連結した結果はインターンされない。
// ただし Append の引数の個々の文字列はインターンされる
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月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar