注
この記事は機能仕様です。 仕様は、機能の設計ドキュメントとして機能します。 これには、提案された仕様の変更と、機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が最終決定され、現在の ECMA 仕様に組み込まれるまで公開されます。
機能の仕様と完成した実装の間には、いくつかの違いがある可能性があります。 これらの違いは、関連する 言語設計会議 (LDM) ノートでキャプチャされます。
機能仕様を C# 言語標準に導入するプロセスの詳細については、仕様に関する記事を参照してください。
チャンピオン号: https://github.com/dotnet/csharplang/issues/7104
概要
System.Threading.Lock
が lock
キーワードと対話する特殊なケース (内部でEnterScope
メソッドを呼び出す)。
静的分析の警告を追加して、可能な限り型の誤用を防ぎます。
モチベーション
.NET 9 では、既存のモニター ベースのロックの代わりにSystem.Threading.Lock
型が導入されています。
C# に lock
キーワードが存在すると、開発者はこの新しい型で使用できる可能性があると考える可能性があります。
そうすることで、この型のセマンティクスに従ってロックされることはありませんが、代わりに他のオブジェクトとして扱われ、モニターベースのロックが使用されます。
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
詳細な設計
lock ステートメント (§13.13) のセマンティクスは、 System.Threading.Lock
型の特殊なケースに変更されます。
フォームの
lock
ステートメントlock (x) { ... }
- ここで、
x
はSystem.Threading.Lock
型の式であり、次の式と正確に等しくなります。およびusing (x.EnterScope()) { ... }
System.Threading.Lock
には次の図形が必要です。namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }
- ここで、
x
は reference_typeの式であり、正確には [...] と同じです。
図形が完全にチェックされていない可能性があることに注意してください (たとえば、 Lock
型が sealed
されていない場合はエラーも警告も発生しません)、機能が期待どおりに動作しない可能性があります (たとえば、 Lock
を派生型に変換する場合、その機能は派生型がないことを前提としているため、警告はありません)。
さらに、型をアップキャストするときに、暗黙的な参照変換 (System.Threading.Lock
) に新しい警告が追加されます。
暗黙的な参照変換は次のとおりです。
- 任意の reference_type から
object
とdynamic
まで。
- reference_typeが
System.Threading.Lock
であることが判明すると、警告が報告されます。- が
S
から派生している場合、任意のclass_typeT
から任意のS
T
に渡されます。
S
がSystem.Threading.Lock
であることが判明すると、警告が報告されます。- 任意のclass_type
S
から任意のinterface_typeT
まで、S
を実装T
提供されます。
S
がSystem.Threading.Lock
であることが判明すると、警告が報告されます。- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
この警告は、同等の明示的な変換でも発生します。
コンパイラは、 object
に変換した後にインスタンスをロックできない場合に警告を報告することを回避します。
- 変換が暗黙的であり、オブジェクト等価演算子の呼び出しの一部である場合。
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
警告から抜け出し、モニターベースのロックを強制的に使用するには、
- 通常の警告抑制手段 (
#pragma warning disable
) -
Monitor
API を直接、 -
object AsObject<T>(T l) => (object)l;
などの間接キャスト。
選択肢
他の型が
lock
キーワードとの対話に使用できる一般的なパターンをサポートします。 これは、ref struct
がジェネリックに参加できる場合に実装される可能性のある将来の作業です。 LDM 2023-12-04 で説明。既存のモニター ベースのロックと新しい
Lock
(または将来のパターン) の間のあいまいさを回避するには、次の方法があります。- 既存の
lock
ステートメントを再利用する代わりに、新しい構文を導入します。 - 新しいロックの種類を
struct
する必要があります (既存のlock
では値型が許可されていないので)。 構造体に遅延初期化がある場合は、既定のコンストラクターとコピーに問題がある可能性があります。
- 既存の
codegen は、スレッドの中止 (それ自体は廃止されています) に対して強化される可能性があります。
また、
Lock
が型パラメーターとして渡されたときにも警告する可能性があります。これは、型パラメーターのロックでは常にモニター ベースのロックが使用されるためです。M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }
ただし、これにより、望ましくない
Lock
をリストに格納するときに警告が発生します。List<Lock> list = new(); list.Add(new Lock()); // would warn here
System.Threading.Lock
を使用してusing
でawait
を使用しないように静的分析を含めることができます。 つまり、using (lockVar.EnterScope()) { await ... }
などのコードに対してエラーまたは警告を出力できます。 現時点では、Lock.Scope
はref struct
であるため、これは必要ありません。そのため、コードは違法です。 しかし、ref struct
メソッドのasync
を許可したり、Lock.Scope
をref struct
に変更したりすると、この分析が有益になります。 (今後実装される場合は、一般的なパターンと一致するすべてのロックの種類についても考慮する必要があります。一部のロックの種類はawait
で使用できる可能性があるため、オプトアウト メカニズムが必要になる場合があります)。または、ランタイムの一部として出荷されるアナライザーとして実装することもできます。値型を
lock
できないという制限を緩和できます- 新しい
Lock
型 (API 提案によってclass
からstruct
に変更された場合にのみ必要) - は、将来実装されるときに任意の型が参加できる一般的なパターンです。
- 新しい
lock
がasync
内で使用されないawait
メソッドで新しいlock
を許可できます。- 現在、
lock
はリソースとしてusing
を使用してref struct
に低下するため、コンパイル時エラーが発生します。 回避策は、lock
を別のasync
以外のメソッドに抽出することです。 -
ref struct Scope
を使用する代わりに、Lock.Enter
Lock.Exit
try
で/メソッドとfinally
メソッドを出力できます。 ただし、Exit
メソッドは、Enter
とは異なるスレッドから呼び出されたときにスローする必要があるため、Scope
を使用するときに回避されるスレッド参照が含まれています。 -
using
本体内にref struct
がない場合は、async
メソッドのawait
でusing
をコンパイルできるようにすることが最善です。
- 現在、
デザインに関する会議
-
LDM 2023-05-01:
lock
パターンをサポートするための最初の決定 - LDM 2023-10-16: .NET 9 のワーキング セットにトリアージ
-
LDM 2023-12-04: 一般的なパターンを拒否し、
Lock
タイプの特殊な大文字と小文字の区別のみを受け入れ、静的分析警告を追加しました
C# feature specifications