- PR -

長い文字列のハードコーディング

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-01-20 12:19
引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:

他にもいくつか書き方はあるでしょうけど、長い文字列をハードコーディングしたい状況ってのはよくあること(主にSQL等)ですから、どんな書き方が最も望ましいかは知っておきたいところです。


SQL に特化するのか、SQL はあくまでも長い文字列の例でしかないのかで違ってくると思います。

SQL や、SQL 以外でもなにか構造を持った文字列ならば、私は SqlStringBuilder のようなクラスを自前で作って、処理します。このクラスはたとえば、setWhere メソッドや addColumn メソッドを持っています。区切りの空白やカンマはこのクラスが自動的に付けてくれます。そんなに凝ったものではなく、数時間で作ってしまえる程度のものです。(3)の豪華版、という感じでしょうか。

構造を意識しなくて良いただの長い文字列ならば、テキトーに連結してしまえばよいだけなので、あまり拘りはないです。ただ、変数名が複数回出現すると、変数名を間違えてしまう可能性が出てくるので、(1)はあまりやりたくないです。
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2008-01-20 14:17
引用:

IIJIMASさんの書き込み (2008-01-20 04:00) より:
引用:

ぼのぼのさんの書き込み (2008-01-20 01:35) より:
このコードだと、インデント用の改行やスペースがそのままSQLに含まれてしまいませんか?


文字列の内容を実行する言語で改行やスペースのために意図した結果が得られない場合は仕方ありませんが、そうでないかぎり、この方法は「見やすく」かけると思います。


SQLを実行するだけなら意図した結果は得られるでしょうね。
しかし、実行前にSQLをログに記録する、としたらどうでしょう?
タブ文字や改行文字がログ上で特殊な意味を持つ場合って、ありますよね。
#これが「そうでないかぎり」の部分になるかな

引用:

引用:

なので、これはどちらかというと少しでもお行儀の良いコードを書きたいという、精神衛生上の問題なのです。
従って、単にパフォーマンスというよりは、可読性とかも含めた議論になるかと思ってます。


であれば、やはり見やすい(2)か長くても1行で書くのがよいと思います。
文字列がSQL等他の言語のコードである場合、プログラムコードファイル上でもなるべくその標準的な形を崩さないものがよいと思います。


可読性まで含めて考えるなら、長くても1行で書くのは果たして読みやすいでしょうか?
私はそうは思えません。

引用:

unibonさんの書き込み (2008-01-20 12:19) より:

SQL に特化するのか、SQL はあくまでも長い文字列の例でしかないのかで違ってくると思います。


スレタイに準ずるなら後者ですね。
ただ、私の過去の経験上、ソース上にハードコーディングする長い文字列が現れた場合、それはたいていSQLであることが多いです。このスレを立てるきっかけになった元スレでもSQLでした。
例えばXMLなんてSQL以上にべた書きすることは少ないし、エラーメッセージなんてのも大抵は長くなっても2〜3行ですしね。

引用:

SQL や、SQL 以外でもなにか構造を持った文字列ならば、私は SqlStringBuilder のようなクラスを自前で作って、処理します。このクラスはたとえば、setWhere メソッドや addColumn メソッドを持っています。区切りの空白やカンマはこのクラスが自動的に付けてくれます。そんなに凝ったものではなく、数時間で作ってしまえる程度のものです。(3)の豪華版、という感じでしょうか。


これはパフォーマンスや可読性よりも、拡張性や汎用性の方に関わる話ですね。
確かに単テーブルを単純な条件で参照、更新するだけならこの方法がいいかもしれません。
ただ、サブクエリやUNIONやWITH句を使った複雑なSQLでは、おそらくこのクラス使えませんよね?そういう場合、どう書くかという話です。
SQLに特化して言うなら、パラメータクエリを使った場合CommandTextは固定になることも多いですしね。


パフォーマンスはあまり気にしてないと言っといてなんですが、この話の要点って、内部でmallocが何回走るかってとこが肝だと思うですよ。それはMSILのコードをあけてまで追求することではなくて、単純に基礎知識としておさえておきたいというレベルだったです。
で、(1)なら4回、(2)なら1回、(3)なら2回ということなら(2)でいいなと思うし、(1)なら4回、(2)も4回、(3)なら2回ということなら(3)もしくはconstつけて(4)がいいかなと思うし、一番知りたかったのはそこだったりします。
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2008-01-20 17:16
引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:
素朴な疑問1。
本件は長い文字列を複数行に記述する時の話だと思うのですが、この場合、逐語的リテラルは何か関係あるのでしょうか?


引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:
素朴な疑問2。
コンパイラとか最適化とかあんまり詳しくないのですが、下記(1)〜(3)で、コンパイル後、実際に動くときのリソースの使われ方とかって、変わってくるものでしょうか?いや、普通に考えれば全く同じってことはないですよね。(3)は使ってるクラスが違うわけですから。

コード:
(1)
  [VB]
    Dim sql As String = ""
    sql += "SELECT C1, C2, C3"
    sql += " FROM T1"
    sql += " WHERE C1 = @C1"
  [C#]
    string sql = "";
    sql += "SELECT C1, C2, C3";
    sql += " FROM T1";
    sql += " WHERE C1 = @C1";

(2)
  [VB]
    Dim sql As String = "" _
        + "SELECT C1, C2, C3" _
        + " FROM T1" _
        + " WHERE C1 = @C1"
  [C#]
    string sql = ""
        + "SELECT C1, C2, C3"
        + " FROM T1"
        + " WHERE C1 = @C1";

(3)
  [VB]
    Dim sb As New StringBuilder()
    sb.Append("SELECT C1, C2, C3")
    sb.Append(" FROM T1")
    sb.Append(" WHERE C1 = @C1")
    Dim sql As String = sb.ToString()
  [C#]
    StringBuilder sb = new StringBuilder();
    sb.Append("SELECT C1, C2, C3");
    sb.Append(" FROM T1");
    sb.Append(" WHERE C1 = @C1");
    string sql = sb.ToString();




引用:

todoさんの書き込み (2008-01-19 21:38) より:
コード:
(4)
  [C#]
    const string sql = ""
        + "SELECT C1, C2, C3"
        + " FROM T1"
        + " WHERE C1 = @C1";





○可読性の観点から。
長い文字列を記述する際、1行で書ききるのは可読性が悪くなるので、
多くの人は複数行で書きたいのだと思います。
ここでは実際に生成されるSQL文にも改行等を含ませるかどうかは
ポリシーによるところだと思いますので、横に置いときます。
(DB側のSQL文のキャッシュを考慮するなら、プロジェクトで規定が必要かもしれませんね)

で、可読性を考慮すると複数行で書いた方がよいと考えるのでしょうけれど、
可読性は高めつつもなるべくならパフォーマンスの低下を招く可能性のあるものは
避けたいというのが心情かと思います。

○パフォーマンスの観点から。
(a) 使用する文字列の個数は少ない方がよい。
(b) 文字列を連結する場合はStringBuilderを使う方がよい。

(a)の理由は、使用した文字列は、文字列インターンプールに溜まっていくから。
(b)の理由は、Stringで結合をする場合、何度もメモリ確保が行われるから。

C#の逐語的リテラル(いわゆる複数行文字列のリテラル表現ですね)では、
複数行でありながら、あくまで一つの文字列として扱われます。
(便宜上、(0)とします)

(0)では、(a)は1個、(b)は連結がありませんので該当なしです。
(1)では、(a)は5個、(b)は3回です。
(2)では、(a)は5個、(b)はStringでの連結はなしです。
(3)は(2)と同様です。

(2)と(3)はおそらく等価になるように最適化されると思いますが、
正確なところはildasmで確認した方がよいですね。
(4)は定数なのでひょっとすると(0)と同じように最適化されるのかなと思いますが
正確なところはildasmで確認した方がよいですね。

引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:
他にもいくつか書き方はあるでしょうけど、長い文字列をハードコーディングしたい状況ってのはよくあること(主にSQL等)ですから、どんな書き方が最も望ましいかは知っておきたいところです。話題としては既出かもしれませんが、よろしければご意見お聞かせください。



長文と言えるものは、SQLとエラーメッセージくらいでしか見た事はないですが、
ぶっちゃけ、相対的にはパフォーマンスに影響する程のものでもないので、
パフォーマンス観点は度外視でもかまわないと思います。

そういう意味で、(1)か(2)ですね。
パフォーマンスをちらっと気にするなら(2)で。
(3)は純粋に見難いと思うのでパス。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2008-01-20 20:07
こんばんは。

数値リテラル同士の演算と同様に
文字列リテラル同士の結合なんて、実行時でなくてコンパイル時にやっちゃうでしょう!?

引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:
素朴な疑問2。
コンパイラとか最適化とかあんまり詳しくないのですが、下記(1)〜(3)で、コンパイル後、実際に動くときのリソースの使われ方とかって、変わってくるものでしょうか?いや、普通に考えれば全く同じってことはないですよね。(3)は使ってるクラスが違うわけですから。

コード:
(1)
  [VB]
    Dim sql As String = ""
    sql += "SELECT C1, C2, C3"
    sql += " FROM T1"
    sql += " WHERE C1 = @C1"
  [C#]
    string sql = "";
    sql += "SELECT C1, C2, C3";
    sql += " FROM T1";
    sql += " WHERE C1 = @C1";

(2)
  [VB]
    Dim sql As String = "" _
        + "SELECT C1, C2, C3" _
        + " FROM T1" _
        + " WHERE C1 = @C1"
  [C#]
    string sql = ""
        + "SELECT C1, C2, C3"
        + " FROM T1"
        + " WHERE C1 = @C1";

(3)
  [VB]
    Dim sb As New StringBuilder()
    sb.Append("SELECT C1, C2, C3")
    sb.Append(" FROM T1")
    sb.Append(" WHERE C1 = @C1")
    Dim sql As String = sb.ToString()
  [C#]
    StringBuilder sb = new StringBuilder();
    sb.Append("SELECT C1, C2, C3");
    sb.Append(" FROM T1");
    sb.Append(" WHERE C1 = @C1");
    string sql = sb.ToString();


他にもいくつか書き方はあるでしょうけど、長い文字列をハードコーディングしたい状況ってのはよくあること(主にSQL等)ですから、どんな書き方が最も望ましいかは知っておきたいところです。話題としては既出かもしれませんが、よろしければご意見お聞かせください。



バージョン・言語/コンパイラ・ビルドオプションによって多少違うでしょうが…
それぞれILを見てみるとこんな感じ

IL (1)
コード:
    .maxstack 2
    .locals init (
        [0] string sql)
    L_0000: nop 
    L_0001: ldstr ""
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldstr "SELECT C1, C2, C3"
    L_000d: call string [mscorlib]System.String::Concat(string, string)
    L_0012: stloc.0 
    L_0013: ldloc.0 
    L_0014: ldstr " FROM T1"
    L_0019: call string [mscorlib]System.String::Concat(string, string)
    L_001e: stloc.0 
    L_001f: ldloc.0 
    L_0020: ldstr " WHERE C1 = @C1"
    L_0025: call string [mscorlib]System.String::Concat(string, string)
    L_002a: stloc.0 



IL (2)
コード:
    .maxstack 1
    .locals init (
        [0] string sql)
    L_0000: nop 
    L_0001: ldstr "SELECT C1, C2, C3 FROM T1 WHERE C1 = @C1"
    L_0006: stloc.0 



IL (3)
コード:
    .maxstack 2
    .locals init (
        [0] class [mscorlib]System.Text.StringBuilder sb,
        [1] string sql)
    L_0000: nop 
    L_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldstr "SELECT C1, C2, C3"
    L_000d: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0012: pop 
    L_0013: ldloc.0 
    L_0014: ldstr " FROM T1"
    L_0019: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_001e: pop 
    L_001f: ldloc.0 
    L_0020: ldstr " WHERE C1 = @C1"
    L_0025: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_002a: pop 
    L_002b: ldloc.0 
    L_002c: callvirt instance string [mscorlib]System.Object::ToString()
    L_0031: stloc.1 



じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-01-21 09:41
引用:

ぼのぼのさんの書き込み (2008-01-19 18:28) より:

展開されないので別スレ立ててみましたw


ごめっw 年末年始が忙しかったので忘れておりました。 当初の疑問はすでに回答がついていて何だか特に言うことがないわけですがw

どうしてもハード コーディングしないといけない場面はあるわけですが、C# なら逐語的リテラル、VB なら 1 行で書くことが多いです。 C# は見た目と編集しやすさ重視です。 VB は逐語的リテラルがなく見やすさと編集のしやすさが両立できる書法がないので 1 行にしています。

C# の逐語的リテラルですが、間にスペースが入るのは気にしません。 あってもなくても動作に影響がないので可読性を優先します。 気になるのであれば HTML <PRE> 要素みたく左に詰めて書くのもアリではないでしょうか。 私は見た目のインデントを気にしたいです。

1 行で書く場合については SQL エディタとセット前提です。 編集がある場合は SQL エディタへコピペして編集し、再度ソースへコピペして終了なので、コメントさえ書いておけばあまり問題にはならないです。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2008-01-21 10:07
VB.netおよびVisualStudio2005限定の話ですみませんが、コードスニペットの挿入で
[データ型 - Visual Basicによって定義済み] > [複数行文字列リテラルの生成] を
選ぶと、こんなコードが生成されます。

コード:
        Dim longString As String
        longString = _
            "This is the first line of my string." & ControlChars.CrLf & _
            "This is the second line of my string." & ControlChars.CrLf & _
            "This is the third line of my string."



また、MSDNの+= 演算子 (Visual Basic)の項目にこんなメモがありました。
引用:

メモ : += 演算子を使用すると、加算と文字列連結のどちらが行われるのか、
事前にはわかりにくい場合があります。連結に &= 演算子を使用することで、
あいまいさがなくなり、プログラムの可読性が向上します。



一応この方法がVB.netではMicrosoft的にお勧めな方法なのかな? と理解しています。
(MSILなどで裏づけをとったわけではありませんが)
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2008-01-21 10:16
引用:

よねKENさんの書き込み (2008-01-20 17:16) より:

そういう意味で、(1)か(2)ですね。
パフォーマンスをちらっと気にするなら(2)で。
(3)は純粋に見難いと思うのでパス。




すっかり忘れていましたが、VBは複数行を継続して記述する際は、
行継続文字(スペース+"_")を付加する必要があるのとその回数に制限があるので、
VBに関しては(1)の記述方法がお勧めですね。SQLに関して言えば修正もしやすいので。
(パフォーマンスは気にしない方向で。)
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2008-01-21 10:42
引用:

よねKENさんの書き込み (2008-01-21 10:16) より:

すっかり忘れていましたが、VBは複数行を継続して記述する際は、
行継続文字(スペース+"_")を付加する必要があるのとその回数に制限があるので、



回数制限があるという話は初めて聞いたので調べてみました。

・VBAでこんな話題があがっていました。
エラー : 行接続文字を使いすぎています
・MSDN(VisualStudio2005に付属のもの)では、特にそのような説明は見当たりませんでした。
・VB.net2005で行継続文字を使って1000行くらい連結してみましたが、ビルドは通りました。

VB6以前だと発生するが、VB.netになって大丈夫になったのかな? と予想しましたが、
VB6の確認環境を持っていないのでわかりません。

スキルアップ/キャリアアップ(JOB@IT)