次の方法で共有


"パターン ベースの using" と "using 宣言"

メモ

この記事は機能仕様についてです。 仕様は、機能の設計ドキュメントとして使用できます。 これには、提案された仕様の変更および機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が決定され、現在の ECMA 仕様に組み込まれるまで公開されます。

機能の仕様と行われた実装では、いくつかの違いがあることがあります。 これらの違いは、関連する言語設計ミーティング (LDM) メモに取り上げられています。

機能仕様を C# 言語標準に導入するプロセスの詳細については、仕様に関する記事を参照してください。

チャンピオンの課題: https://github.com/dotnet/csharplang/issues/114

まとめ

この言語では、リソース管理を簡単にするために、using ステートメントの周囲に 2 つの新機能が追加されます。using は、IDisposable に加えて破棄可能なパターンを認識し、using 宣言を言語に追加します。

目的

using ステートメントは、今日のリソース管理に効果的なツールですが、かなりの形式的な処理が必要です。 管理するリソースが多数あるメソッドでは、一連の using ステートメントでは構文的に行き詰まる可能性があります。 このような構文上の負担が大きいため、ほとんどのコーディング スタイル ガイドラインでは、このシナリオに対して中括弧に関する例外を明示的に規定しています。

using 宣言はここでの形式的な処理の多くを削除し、リソース管理ブロックを含む他の言語と同等の C# を取得します。 さらに、パターンベースの using を使用すると、開発者はここで参加できる型のセットを拡張できます。 多くの場合、using ステートメントで値を使用できるようにするためにのみ存在するラッパー型を作成する必要がなくなります。

これらの機能を組み合わせることで、開発者は using を適用できるシナリオを簡素化および拡張できます。

詳細な設計

using 宣言

この言語を使用すると、ローカル変数宣言に using を追加できます。 このような宣言は、同じ場所にある using ステートメントで変数を宣言する場合と同じ効果を持ちます。

if (...) 
{ 
   using FileStream f = new FileStream(@"C:\source\using.md");
   // statements
}

// Equivalent to 
if (...) 
{ 
   using (FileStream f = new FileStream(@"C:\source\using.md")) 
   {
    // statements
   }
}

using ローカルの有効期間は、それが宣言されているスコープの最後まで延長されます。 その後、using ローカルは宣言されている順序の逆に破棄されます。

{ 
    using var f1 = new FileStream("...");
    using var f2 = new FileStream("...");
    using var f3 = new FileStream("...");
    ...
    // Dispose f3
    // Dispose f2 
    // Dispose f1
}

using 宣言に直面しても、goto やその他の制御フロー コンストラクトに関する制限はありません。 代わりに、コードは同等の using ステートメントの場合と同じように機能します。

{
    using var f1 = new FileStream("...");
  target:
    using var f2 = new FileStream("...");
    if (someCondition) 
    {
        // Causes f2 to be disposed but has no effect on f1
        goto target;
    }
}

using ローカル宣言で宣言されたローカルは、暗黙的に読み取り専用になります。 これは、using ステートメントで宣言されたローカルの動作と一致します。

using 宣言の言語文法は次のようになります。

local-using-declaration:
  'using' type using-declarators

using-declarators:
  using-declarator
  using-declarators , using-declarator
  
using-declarator:
  identifier = expression

using 宣言に関する制限事項:

  • case ラベル内に直接表示されるのではなく、case ラベル内のブロック内に配置する必要があります。
  • out 変数宣言の一部として表示されない場合があります。
  • 各宣言子の初期化子が必要です。
  • ローカル型は、暗黙的に IDisposable に変換可能であるか、using パターンを満たす必要があります。

パターンベースの using

言語は、ref struct 型の破棄可能なパターンの概念を追加します。これは、アクセス可能な Dispose インスタンス メソッドを持つ ref struct です。 破棄可能なパターンに適合する型は、IDisposable の実装を必要とせずに、using ステートメントまたは宣言に参加できます。

ref struct Resource
{ 
    public void Dispose() { ... }
}

using (var r = new Resource())
{
    // statements
}

これにより、開発者はusingref struct 型に活用できます。 これらの型は C# 8 でインターフェイスを実装できないため、using ステートメントに参加できません。

ここでは、従来の using ステートメントと同じ制限が適用されます。using で宣言されたローカル変数は読み取り専用で、null 値では例外がスローされません。コード生成は、Dispose を呼び出す前に IDisposable にキャストされないという点でのみ異なります。

{
	  Resource r = new Resource();
	  try {
		    // statements
	  }
	  finally {
		    if (r != null) r.Dispose();
	  }
}

破棄可能なパターンに合わせるには、Dispose メソッドはアクセス可能なインスタンス メンバーであり、パラメーターなしであり、void 戻り値の型がある必要があります。 拡張メソッドにすることはできません。

考慮事項

これらの考慮事項はどちらも C# 8 では実装されませんでした

ブロックなしのケースラベル

using declaration は、その実際の寿命に関する問題のため、case ラベル内で直接使用することはできません。 考えられる解決策の 1 つは、単に同じ場所の out var と同じ有効期間を設定することです。 機能実装の複雑さが増し、回避策の容易さ (case ラベルにブロックを追加するだけ) は、このルートを取ることを正当化しなかったと見なされました。

今後の拡張

fixed ローカル

fixed ステートメントには、using ローカルを持つ機能の根拠となった using ステートメントのすべての特性があります。 この機能をローカル fixed に拡張することにも考慮する必要があります。 有効期間と順序付け規則は、ここで usingfixed に同じように適用されます。