C# 10と.NET 6で10分以内に定数補間する
新しいバージョンが出るたびに、C#はパワーとエレガンスの両面で進化を続け、開発者によりクリーンで効率的なコード記述方法を提供しています。 C# 10で追加された機能として、定数の内部で文字列補間を使用できる定数補間文字列があります。
この機能が追加される前は、補間を使って定数文字列を作成しようとすると(たとえば、$"{companyName} Products")、コンパイラがエラーを投げていました。 const値にはプレーンな文字列リテラルしか割り当てることができず、定数メッセージや属性文字列を作成する際に、繰り返しの多い不便なコードになりがちでした。
C# 10と.NET 6以降では、コンパイル時に直接補間文字列を使用して定数を結合できるようになりました。この機能強化により、コードがすっきりするだけでなく、プログラムが実行される前にすべての値が評価されるため、効率的なコードが維持されます。
この仕組みをより明確に理解するために、ティム・コーリー(Tim Corey)氏のビデオ"C# 10 and .NET 6 in 10 Minutes or Less"では、そのコンセプトをステップごとに説明しています。いつもの簡潔で実用的な教え方で、Tim はこの新機能が何をするのか、従来の文字列連結とどのように違うのか、そして特に属性でどのように役立つのかを説明します。
それでは、C#の定数補間文字列がどのように機能するのか、なぜ開発者にとって便利な追加機能なのか、ティムの説明を見ていきましょう。
はじめに
このビデオでティムは、補間文字列(おなじみのドル記号($)構文を使用した文字列)が、以前は不可能だった定数式で使用できるようになったことを説明しています。 この小さな変更により、定数文字列の作成がより簡単に、よりきれいに、より保守的になります。
.NET 6 における定数の探求
Timは、.NET 6のコンソールアプリケーションを開き、定数定義だけに集中するために定型的なコードを削除します。
彼は、最も基本的な定数文字列の宣言から始めます:
const string companyName = "Acme";const string companyName = "Acme";これは、const文字列に代入された単純な文字列リテラルです。 このような定数はコンパイル時に評価されるため、その値は固定され、コンパイルされたプログラムに組み込まれます。
しかし、ティムはすぐに中心的な疑問に移る:定数文字列をマージしたり、補間を使用して他の文字列の中に直接値を埋め込んだりしたい場合はどうすればよいでしょうか。
C# 9以前では、このようなことはできませんでした:
const string productName = $"{companyName} Anvils"; // Not allowed before C# 10const string productName = $"{companyName} Anvils"; // Not allowed before C# 10文字列補間は定数式ではサポートされていないため、この行はコンパイル時にエラーを投げます。
C#10における定数補間文字列
ティムが示すように、C# 10では、コンパイラは定数補間文字列をサポートするようになりました。
そのため、次の例は完璧に機能します:
const string productName = $"{companyName} Anvils";const string productName = $"{companyName} Anvils";これは定数補間文字列であり、コンパイラが実行時ではなくコンパイル時に補間文字列を評価することを意味します。プログラム実行時に余分な文字列の連結や文字列のフォーマットは行われず、コンパイラは "Acme Anvils "のような単一の定数文字列リテラルを生成します。
ティムは、companyNameの値を "Acme "から "ABC "に変更すると、コンパイラは自動的にproductNameに "ABC Anvils "を生成すると説明しています。 コンパイル時の文字列構築であり、実行時の補間ではありません。
この機能強化により、+連結や手作業による値の繰り返しに頼ることなく、定数文字列のマージが非常に簡単になりました。
入れ子の定数補間
ティムはさらに一歩踏み込んで、別の定型的な定義を行っている:
const string productDescription = $"{productName} are the best way to crush unsuspecting roadrunners.";const string productDescription = $"{productName} are the best way to crush unsuspecting roadrunners.";これは入れ子補間の例で、定数補間された文字列(productName)が別の文字列の内部で使用されています。
コンパイラーは、これらをすべて定数式として扱い、コンパイル時に単一の不変の文字列表現を生成します。
ティムがプログラムを実行すると、出力は次のようになります:
Acme Anvils are the best way to crush unsuspecting roadrunners.Acme Anvils are the best way to crush unsuspecting roadrunners.これにより、定数補間が複数の定数にわたってもシームレスに機能することが確認されました。
定数が重要な理由
この時点で、ティムは定数、そして現在は定数補間文字列が有益である理由を説明するために一時停止します。
彼は、定数の値はメモリ上に個別のインスタンスとしてではなく、コンパイルされたコードに直接格納されるため、メモリ効率が非常に高いことを指摘しています。
対照的に、開発者が以前同様のものを必要としていたときは、読み取り専用フィールドを使用することがよくありました:
readonly string companyName = "Acme";readonly string companyName = "Acme";しかし、ティムは、読み取り専用フィールドはconstと同じではないことに注意してください - これらは実行時に評価されるため、より多くのメモリを消費し、コンパイル時の最適化を妨げます。
定数補間文字列を使用することで、コンパイル時の定数のまま、表現力豊かで再利用可能な書式付き文字列を記述できるようになり、明快さとパフォーマンスの両方が向上しました。
実例 - 属性で定数を使用する
ティムは次に、この新機能が輝く実世界のシナリオ、属性を紹介します。
彼は、Main()内で単純なローカル・メソッドを定義しています:
void SayHi() { }void SayHi() { }次に、ある変数を参照する文字列メッセージで[Obsolete]属性を適用しようとする:
string myCompany = "Tim's Company";
[Obsolete($"This is no longer used for {myCompany}")]string myCompany = "Tim's Company";
[Obsolete($"This is no longer used for {myCompany}")]属性はパラメータとして定数式しか受け付けないため、これは失敗します。 myCompanyは定数ではなく変数なので、コンパイラはエラーを生成します。
Timは、属性のメッセージはコンパイル時に一定である必要があり、実行時の値やインスタンス変数に依存することはできないと説明しています。
しかし、C# 10の定数補間文字列のおかげで、これを安全に行うことができるようになりました:
const string productName = $"{companyName} Anvils";
[Obsolete($"This is no longer used for {productName}")]const string productName = $"{companyName} Anvils";
[Obsolete($"This is no longer used for {productName}")]ここでは、コンパイラはcompanyNameとproductNameの両方が定数であることを認識するため、補間された文字列全体が定数式になります。
コンパイラは、コンパイル時にフォーマットされた文字列を生成し、属性内で有効にします。
この例は、定数補間が単なる構文上の糖分ではなく、フォーマットされたコンパイル時の文字列を属性やメタデータで直接使用するような新しいシナリオを可能にする理由を完璧に浮き彫りにしています。
舞台裏 - コンパイラがどのように処理するか
ティムはビデオの中で低レベルのコンパイラ内部には触れませんが、このコンセプトはC# 10で補間文字列ハンドラがどのように機能するかと密接に結びついています。
一般的に、コンパイラは補間文字列に遭遇すると、フォーマット文字列操作に似たコードを作成し、AppendLiteral()やAppendFormatted()のような呼び出しを裏で生成します。
しかし、定数補間文字列を扱う場合、コンパイラーはコンパイル時にすべてを評価するため、生成されるILコードに補間文字列ハンドラーやメソッド呼び出しは含まれません。
つまり、結果として得られる値は、文字列リテラルとまったく同じように動作しますが、他の定数からの埋め込み式を使って合成することができます。
コンパイラが文字列の構築を静的に処理するため、実行時のコストはゼロです。
定数補間された文字列をいつ使うか
ティムは、誰もが毎日この機能を使うわけではないことを認めています。 定数の定義や属性の記述をほとんどしない開発者は、すぐに利益を得られないかもしれません。
しかし、多くのコンパイル時定義、定数メッセージ、属性メタデータを作成する人にとって、この機能はコードを簡素化し、文字列連結の乱雑さを防ぎます。
定数は不変であり、コンパイラによってチェックされるため、動的に連結された文字列や誤って管理された変数によるエラーがなくなります。
これにより、コードがより堅牢になり、読みやすくなり、保守が容易になります。
結論
ティムは、ビデオを締めくくりながら、開発者たちに、自分のプロジェクトで定数補間文字列を使うかどうか考えてみてほしいと呼びかけています。 コンパイル時にフォーマットされた文字列をよりきれいにするために必要不可欠であると考える人もいれば、些細な利便性と考える人もいるでしょう。
いずれにせよ、ティムのデモンストレーションは、この機能を実装する方法と効果的に適用する場所を正確に示しています。
要約すると
定数補間文字列は、定数内の補間式を可能にします。
コンパイル時に評価され、効率的な文字列リテラルを生成します。
繰り返しの多い文字列の連結を、よりきれいな構文に置き換えます。
- 特に、属性、定数ベースの設定、メタデータ・メッセージに役立ちます。
読みやすさとコンパイル時の安全性を両立させたC# 10の定数補間は、表現力豊かで効率的なC#プログラミングへの新たな一歩です。

