文字列を変更したはずが変わらない理由
string型は不変(イミュータブル)です。ToUpperやReplace、Trimなどのメソッドは新しい文字列を返すだけで、元の変数は書き換えません。
なぜエラーが出ないのか
出力: hello
(エラーなし)- C#は何も報告しません。文法として正しいためです
出力: hello- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 class Program { 2 static void Main(string[] args) { 3 string s = "hello"; 4 s.ToUpper() ; ^ 5 System.Console.WriteLine(s); 6 } 7 }
文字列は不変(immutable)です。ToUpper()は新しい文字列を返すだけで、元のsは変わりません。結果を使うには再代入が必要です。
直し方: s.ToUpper() を s = s.ToUpper() にします。
パターン2
1 class Program { 2 static void Main(string[] args) { 3 string s = "a-b-c"; 4 s.Replace("-", " ") ; ^ 5 System.Console.WriteLine(s); 6 } 7 }
Replaceも新しい文字列を返す関数です。呼び出しただけでは元の変数は書き換わりません。
直し方: s.Replace("-", " ") を s = s.Replace("-", " ") にします。
パターン3
1 class Program { 2 static void Main(string[] args) { 3 string s = " hi "; 4 s.Trim() ; ^ 5 System.Console.WriteLine("[" + s + "]"); 6 } 7 }
Trim()も例外ではありません。前後の空白を取り除いた新しい文字列を返すだけです。
直し方: s.Trim() を s = s.Trim() にします。
パターン4
1 class Program { 2 static void Main(string[] args) { 3 string s = "abc"; 4 s.PadLeft(5, '0') ; ^ 5 System.Console.WriteLine(s); 6 } 7 }
PadLeftも新しい文字列を返すだけです。結果を使うには再代入が必要です。
直し方: s.PadLeft(5, '0') を s = s.PadLeft(5, '0') にします。
パターン5
1 class Program { 2 static void Main(string[] args) { 3 string s = "Hello"; 4 s.Substring(0, 3) ; ^ 5 System.Console.WriteLine(s); 6 } 7 }
Substringも例外ではなく、新しい文字列を返すだけの関数です。
直し方: s.Substring(0, 3) を s = s.Substring(0, 3) にします。
よくある誤解
配列やListのメソッドは自分自身を変更するものが多いため、文字列のメソッドも同様だと誤解しがちです。結果を使うには戻り値を変数に代入し直す必要があります。
実務での勘所
文字列の結合を繰り返すコストを避けるにはStringBuilderを使うのが定石ですが、C# 10以降は$"..."という文字列補間の内部実装も改善されています。以前は$"合計: {total}"のような補間はstring.Formatの呼び出しに変換されていましたが、現在はDefaultInterpolatedStringHandlerという専用の仕組みを経由し、中間的な文字列オブジェクトを余分に作らずに結果を組み立てるよう最適化されています。単純な補間であれば、以前ほどStringBuilderとの性能差を気にしなくてよい場面が増えています。