Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
objekt
Poznámka
Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.
Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v příslušných poznámkách ze schůzky návrhu jazyka (LDM) .
Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .
Problém šampiona: https://github.com/dotnet/csharplang/issues/7104
Shrnutí
Zvláštní případ, jak System.Threading.Lock interaguje s klíčovým slovem lock (voláním jeho metody EnterScope na pozadí).
Pokud je to možné, přidejte upozornění statické analýzy, abyste zabránili náhodnému zneužití typu.
Motivace
.NET 9 představuje nový typ System.Threading.Lock jako lepší alternativu k existujícímu uzamčení založenému na monitorování.
Přítomnost klíčového slova lock v jazyce C# může vést vývojáře k tomu, aby si mysleli, že ho můžou používat s tímto novým typem.
Tím by nedošlo k uzamčení podle sémantiky tohoto typu, ale bylo by s ním zacházeno jako s jakýmkoli jiným objektem a bylo by použito uzamčení na základě monitoru.
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
Podrobný návrh
Sémantika příkazu zámku (§13.13) jsou změněny tak, aby speciálně zpracovaly typ System.Threading.Lock.
Příkaz
locktypulock (x) { ... }
- , kde
xje výraz typuSystem.Threading.Lock, je přesně ekvivalentní:ausing (x.EnterScope()) { ... }System.Threading.Lockmusí mít následující tvar:namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }- kde
xje výraz typu reference , je přesně ekvivalentní: [...]
Všimněte si, že tvar nemusí být plně ověřen (např. pokud typ Lock není sealed), ale funkce nemusí fungovat podle očekávání (například při převodu Lock na odvozený typ nebudou vydána žádná upozornění, protože funkce předpokládá neexistenci odvozených typů).
Kromě toho jsou při povyšování typu přidána nová upozornění (System.Threading.Lock):
Implicitní převody odkazů jsou:
- Z libovolného reference_type do
objectadynamic.
- je hlášeno upozornění, pokud je známo, že reference_type je
System.Threading.Lock.- Z libovolného class_type
Sdo libovolného class_typeT, za předpokladu, žeSje odvozeno zT.
- je hlášeno upozornění, když
Sje známo, že jeSystem.Threading.Lock.- Z libovolného class_type
Sna libovolný interface_typeT, za předpokladu, žeSimplementujeT.
- je hlášeno upozornění, když
Sje známo, že jeSystem.Threading.Lock.- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
Všimněte si, že k tomuto upozornění dochází i u ekvivalentních explicitních převodů.
Kompilátor se v některých případech vyhne hlášení upozornění v případě, že po převodu na objectnejde instanci uzamknout:
- pokud je převod implicitní a součástí vyvolání operátoru rovnosti objektu.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
Pokud chcete utéct z upozornění a vynutit použití uzamčení založeného na monitorování, můžete použít
- obvyklé prostředky potlačení upozornění (
#pragma warning disable), - přímo rozhraní
MonitorAPI - nepřímé přetypování jako
object AsObject<T>(T l) => (object)l;.
Alternativy
Podpora obecného vzoru, který mohou používat i jiné typy k interakci s klíčovým slovem
lock. Jedná se o budoucí práci, která by mohla být implementována, když seref structmohou účastnit generických typů. Popsáno v LDM 2023-12-04.Abychom se vyhnuli nejednoznačnosti mezi stávajícím uzamykáním založeným na monitorování a novým
Lock(nebo vzorem v budoucnu), mohli bychom:- Místo opětovného použití existujícího příkazu
lockzavést novou syntaxi. - Vyžadovat, aby nové typy zámků byly
struct(protože stávajícílocknepovolují typy hodnot). Pokud mají struktury opožděné inicializace, může dojít k problémům s výchozími konstruktory a kopírováním.
- Místo opětovného použití existujícího příkazu
Generátor kódu lze zpevnit proti ukončení vláken (které jsou samy o sobě zastaralé).
Můžeme také upozornit, když se
Lockpředává jako parametr typu, protože uzamčení parametru typu vždy používá uzamčení pomocí monitoru:M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }To by však mohlo způsobit upozornění při ukládání
Locks do seznamu, který je nežádoucí:List<Lock> list = new(); list.Add(new Lock()); // would warn hereMohli bychom zahrnout statickou analýzu, abychom zabránili použití
System.Threading.Lockvusingaawait. Můžeme například vygenerovat chybu nebo upozornění pro kód, jako jeusing (lockVar.EnterScope()) { await ... }. V současné době to není potřeba, protožeLock.Scopejeref struct, takže kód je přesto neplatný. Pokud bychom ale někdy povoliliref structveasyncmetodách nebo změniliLock.Scopetak, aby to nebylaref struct, tato analýza by byla přínosná. (Pro všechny typy zámků, které odpovídají obecnému vzoru, bychom mohli zvážit při implementaci v budoucnu. I když může být nutné mít mechanismus opt-out, protože některé typy zámků mohou být povoleny pro použití sawait.) Alternativně by to mohlo být implementováno jako analyzátor dodávaný jako součást běhového prostředí.Mohli bychom zmírnit omezení, že typy hodnot nemohou být
lock.- pro nový typ
Lock(pouze pokud ho návrh rozhraní API změnil zclassnastruct), - pro obecný vzor, kde se jakýkoli typ může zapojit při budoucí implementaci.
- pro nový typ
Nové
lockbychom mohli povolit veasyncmetodách, kdy se vawaitnepoužíválock.- Protože je
locksnížen nausings využitímref structjako prostředku, dojde k chybě při kompilaci. Alternativním řešením je extrahovatlockdo samostatné metody oddělené odasync. - Místo použití
ref struct Scopebychom mohli vLock.EnterLock.Exittrygenerovat metody / afinally. Nicméně metodaExitmusí vyvolat výjimku, když je volána z jiného vlákna nežEnter, proto obsahuje vyhledávání vláken, kterému se vyhneme při použitíScope. - Nejlepší je povolit kompilaci
usingnaref structvasyncmetodách, pokud v těleawaitneexistujeusing.
- Protože je
Designérské schůzky
-
LDM 2023-05-01: počáteční rozhodnutí o podpoře modelu
lock - LDM 2023-10-16: zařazeno do pracovní skupiny pro .NET 9
-
LDM 2023-12-04: odmítl obecný vzor, přijal pouze speciální případ pro typ
Locka přidal varování statické analýzy
C# feature specifications