以下のコードでプラス記号を使用すると、いくつの文字列オブジェクトが作成されますか?
String result = "1" + "2" + "3" + "4";
以下のような場合、「1」、「2」、「12」の3つのStringオブジェクトを指定します。
String result = "1" + "2";
Stringオブジェクトは、パフォーマンス向上のためにString Intern Pool / Tableにキャッシュされることも知っていますが、それは問題ではありません。
以下のコードでプラス記号を使用すると、いくつの文字列オブジェクトが作成されますか?
String result = "1" + "2" + "3" + "4";
以下のような場合、「1」、「2」、「12」の3つのStringオブジェクトを指定します。
String result = "1" + "2";
Stringオブジェクトは、パフォーマンス向上のためにString Intern Pool / Tableにキャッシュされることも知っていますが、それは問題ではありません。
回答:
驚いたことに、それは状況によります。
メソッドでこれを行う場合:
void Foo() {
String one = "1";
String two = "2";
String result = one + two + "34";
Console.Out.WriteLine(result);
}
その後、コンパイラはString.Concat
@Joachimが回答したとおりにコードを発行するようです(彼に+1)。
それらを定数として定義すると、例えば:
const String one = "1";
const String two = "2";
const String result = one + two + "34";
または元の質問のように、リテラルとして:
String result = "1" + "2" + "3" + "4";
その後、コンパイラーはそれらの+
兆候を最適化します。これは次と同等です。
const String result = "1234";
さらに、コンパイラーは無関係な定数式を削除し、使用または公開された場合にのみそれらを出力します。たとえば、このプログラム:
const String one = "1";
const String two = "1";
const String result = one + two + "34";
public static void main(string[] args) {
Console.Out.WriteLine(result);
}
定数result
(「1234」に等しい)を1つだけ生成します。 one
そしてtwo
結果としてILには表示されません。
実行時にさらに最適化される可能性があることに注意してください。私は、ILが生成されたものをそのまま使用します。
最後に、インターンに関しては、定数とリテラルがインターンされますが、インターンされる値は、リテラルではなく、ILで結果として得られる定数値です。これは、複数の同一に定義された定数またはリテラルが実際には同じオブジェクトになるため、予想よりも少ない文字列オブジェクトを取得する可能性があることを意味します。これを以下に示します。
public class Program
{
private const String one = "1";
private const String two = "2";
private const String RESULT = one + two + "34";
static String MakeIt()
{
return "1" + "2" + "3" + "4";
}
static void Main(string[] args)
{
string result = "1" + "2" + "34";
// Prints "True"
Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt()));
// Prints "True" also
Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT));
Console.ReadKey();
}
}
文字列がループで(または動的に)連結される場合、連結ごとに1つの余分な文字列が作成されます。たとえば、次は12個の文字列インスタンスを作成します。2つの定数+ 10回の反復で、それぞれ新しいStringインスタンスになります。
public class Program
{
static void Main(string[] args)
{
string result = "";
for (int i = 0; i < 10; i++)
result += "a";
Console.ReadKey();
}
}
しかし(驚くべきことに)、複数の連続した連結がコンパイラーによって1つのマルチストリング連結に結合されます。たとえば、このプログラムは12個の文字列インスタンスも生成します!これは、「1つのステートメントで複数の+演算子を使用しても、文字列の内容は1回だけコピーされるためです。」
public class Program
{
static void Main(string[] args)
{
string result = "";
for (int i = 0; i < 10; i++)
result += "a" + result;
Console.ReadKey();
}
}
string s = ""; for (int i = 0; i < n; i++) s += "a";
クリス・シャインの答えはとても良いです。文字列連結オプティマイザを作成した人として、2つの興味深い点を追加します。
1つ目は、連結オプティマイザが安全に実行できる場合、本質的に括弧と左結合性の両方を無視することです。文字列を返すメソッドM()があるとします。あなたが言うなら:
string s = M() + "A" + "B";
その後、コンパイラーは加算演算子が結合型のままであると判断します。したがって、これは次と同じです。
string s = ((M() + "A") + "B");
しかしこれは:
string s = "C" + "D" + M();
と同じです
string s = (("C" + "D") + M());
これは、定数文字列 "CD"
との連結ですM()
。
実際、連結オプティマイザは文字列連結が連想的であることを認識しString.Concat(M(), "AB")
、最初の例を生成しますが、これは左結合性に違反しています。
あなたもこれを行うことができます:
string s = (M() + "E") + ("F" + M()));
まだ生成しString.Concat(M(), "EF", M())
ます。
2番目の興味深い点は、nullと空の文字列が最適化されることです。したがって、これを行う場合:
string s = (M() + "") + (null + M());
あなたが得るでしょう String.Concat(M(), M())
次に興味深い質問が出されます:これはどうですか?
string s = M() + null;
それを最適化することはできません
string s = M();
理由M()
はnullを返すかもしれませんが、String.Concat(M(), null)
場合は、空の文字列を返すM()
リターンがヌル。だから私たちがすることは代わりに減らすことです
string s = M() + null;
に
string s = M() ?? "";
これにより、文字列の連結が実際に呼び出す必要がないことを示しString.Concat
ます。
この主題の詳細については、以下を参照してください。
("C" + "D") + M())
生成します。さらに下では、ではなくを生成する必要があります。String.Concat("CD", M())
String.Concat(M(), "AB")
(M() + "E") + (null + M())
String.Concat(M(), "E", M())
String.Concat(M(), M())
その答えはMSDNで見つかりました。1。
連結は、ある文字列を別の文字列の末尾に追加するプロセスです。+演算子を使用して文字列リテラルまたは文字列定数を連結すると、コンパイラは単一の文字列を作成します。実行時の連結は発生しません。ただし、文字列変数は実行時にのみ連結できます。この場合、さまざまなアプローチのパフォーマンスへの影響を理解する必要があります。
1つは、静的であるため、コンパイラーはコンパイル時にそれを単一の文字列に最適化できることです。
それらが動的であった場合、それらはString.Concat(string、string、string、string)への単一の呼び出しに最適化されているでしょう。