読者の皆様へ:ノイズを減らすための一連の機能を開発しました。関心のあるトピックについて電子メールとWeb通知を受け取ることができます。新機能の詳細をご覧ください。
比較的小さなリリースだが、C# 7.3はC# 1と2から長い間残った苦情に対応している。
Overload解決
バージョン1.0のC#から、overloadの解決ルールは、疑問の残るデザインであった。いくつかの状況で2つ以上のメソッドが候補になるが、1つしか使用できない。優先順位に基づき、正しくないメソッドが選択されるか、コンパイラーが一致するメソッドを見つけられない、または一致するがあいまいなことがある。
C# 7.3では、オーバーロード解決時にいくつかのチェックがされるため、誤った一致によるコンパイルエラーが発生しなくなった。改善されたoverload候補提案の概要:
1. インスタンスとstaticメンバーの両方が含まれている場合、インスタンスレシーバーかコンテキストなしで呼び出された場合はインスタンスメンバーを破棄し、インスタンスレシーバーがあって呼び出された場合はstaticメンバーを破棄する。レシーバーがない場合、staticコンテキストにstaticメンバーのみを含め、それ以外の時はstaticとインスタンスメンバーの両方を含める。レシーバーがインスタンスか型かがあいまいな時は、両方を含める。明らかにインスタンスレシーバーが使えないstaticコンテキストには、staticメンバーなどのメンバーの本体が定義されていないメンバー、フィールドイニシャライザとコンストラクターイニシャライザなど、これが使用できない場所が含まれる。
2. 型引数が制約を満たさないジェネリックメソッドは、候補セットから削除される。
3. メソッドグループの変換では、候補メソッドが、delegateの戻り型とマッチしない場合、セットから削除される。
ジェネリックの制約: enum, delegate, unmanaged
C# 2.0でジェネリクスが導入されて以来、開発者はenumがジェネリック型にできないことに不満を持っていた。これは最終的に対処され、enum
キーワードをジェネリックとして使用できるようになった。同様にキーワードdelegate
をジェネリック制約に使用できるようになった。
これらは必ずしも期待通りに動作するわけではない。例えばwhere T : enum
制約は、System.Enum
のサブクラスとして使用したいだろうが、Foo<System.Enum>
でも使用できる。とはいえ、enumとdelegateのほとんどのシナリオをカバーするはずである。
Unmanaged型制約は、“unmanaged”キーワードを使った場合、「参照型でなく、ネストのどのレベルでも参照型のフィールドが含まれていない型」である必要がある。 これは「すべてがアンマネージド型で再利用可能なルーチンを作る」必要がある場合の、低レベルなinteropコードで使用される。アンマネージド型には以下が含まれる:
- プリミティブなsbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, IntPtr, UIntPtr
- あらゆるenum型
- ポインター型
- 上記のみが含まれるユーザー定義の構造体
隠しフィールドの属性
自動実装プロパティは非常に便利だが、後方のフィールドに属性を適用できないためそれを見ることができないなど、いくつかの制限がある。通常は問題にならないが、シリアライゼーションを扱うときに問題になることがある。
自動実装されたプロパティフィールドターゲット属性提案は、これをストレートに対処している。自動実装されたプロパティを適用するときは、シンプルにfield:
修飾子を付与する。
[Serializable]
public class Foo {
[field: NonSerialized]
public string MySecret { get; set; }
}
タプル比較(== と !=)
タプル型で== と != をサポートする、この提案の名前は、詳細とエッジケースをうまく要約している。もっとも重要な変更点は
もし誰かが比較演算子を実装したValueTuple型を書いたとしたら、以前はoverload解決によって選ばれていた。しかし新しいタプルでは、overload解決よりも前に、ユーザー定義の比較に頼るのではなく、タプル比較が処理される。
理想としては、カスタムValueTuple型は、C# 7.3コンパイラーと同じルールに従うことだが、ネストされたタプルやdynamic型の処理で微妙な違いが発生する。
イニシャライザの式変数
これはアンチ機能のように見える。機能を追加するのではなく、Microsoftは式変数が使用できる場所の制限を取り除いた。
ctor-initializer内の式変数宣言(out変数宣言と宣言パターン)を禁止する制限を削除した。宣言された変数は、コンストラクタのボディで有効になる。
フィールドまたはプロパティイニシャライザ内の式変数宣言(out変数宣言と宣言パターン)を禁止する制限を削除した。宣言された変数は、イニシャライザ式内で有効になる。
lambdaのボディに変換されるクエリ式内の変数式宣言(out変数宣言と宣言パターン)を禁止する制限を削除した。宣言された変数は、クエリの式内で有効になる。
これらの制限の当初の理由は、単に「時間の不足」を書かれていた。おそらくこの制限により、C# 7の以前のバージョンで必要だったテスト量が削減されたと考えられる。
スタックに割り当てられた配列
まれにしか使われないが、重要なC#の機能は、stackalloc
キーワードでスタック内に割り当てられる配列である。これにより、ヒープへの割り当てとGCの圧迫が起こらないことで、通常の配列はパフォーマンが改善する可能性がある。
int* block = stackalloc int[3] { 1, 2, 3 };
スタックに割り当てられた配列を使うことは危険なことがある。スタックに対応するポイントが必要になった時に、unsafeコンテキストの中でしか使用できない。CLRは、バッファオーバーランを有効にすることで、これを軽減しようとしている。これにより、アプリケーションは「可能な限り迅速に終了する」ようになる。
C# 7.3では、通常の配列と同様に、作成するときに配列を初期化できる。この提案には詳細がないが、Microsoftは機能が実行されたときに迅速にコピーできるマスター配列を事前初期化することを検討している。理論上は、要素ごとに初期化するよりも早くなる。
注意点として、スタックに割り当てられた配列は、多くの小さな、短期の配列が必要なシナリオを対象にしている。大きな配列や、深く再帰的な関数に使用すると、スタック領域を超える可能性があるため、使用してはならない。
スタックに割り当てられたSpan
スタックに割り当てられた配列の安全な代替は、スタックに割り当てられたspanである。ポインターを削除することで、バッファオーバーランも削除できる。これにより、メソッドをunsafeにしなくても使用できる。
Span<int> block = stackalloc int[3] { 1, 2, 3 };
注意点としてSpan<T>
は、System.Memory
NuGetパッケージが必要になる。
再割り当て可能なRefローカル
Refローカルは、通常のローカル変数のように再アサイン可能になった。
他のC# 7.3提案は、csharplang GitHubサイトを参照して欲しい。
Rate this Article
- Editor Review
- Chief Editor Action