Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.
Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de relevante LDM-notities (Language Design Meeting).
Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.
Kampioensprobleem: https://github.com/dotnet/csharplang/issues/8677
Samenvatting
Hiermee staat u toe dat toewijzing voorwaardelijk binnen een a?.b of a?[b] expressie plaatsvindt.
using System;
class C
{
public object obj;
}
void M(C? c)
{
c?.obj = new object();
}
using System;
class C
{
public event Action E;
}
void M(C? c)
{
c?.E += () => { Console.WriteLine("handled event E"); };
}
void M(object[]? arr)
{
arr?[42] = new object();
}
Motivatie
Een verscheidenheid aan motiverende gebruiksvoorbeelden vindt u in het voorvechtende probleem. Belangrijke motivaties zijn onder andere:
- Pariteit tussen eigenschappen en
Set()methoden. - Gebeurtenis-handlers koppelen in UI-code.
Gedetailleerd ontwerp
- De rechterkant van de toewijzing wordt alleen geëvalueerd wanneer de ontvanger van de voorwaardelijke toegang niet null is.
// M() is only executed if 'a' is non-null.
// note: the value of 'a.b' doesn't affect whether things are evaluated here.
a?.b = M();
- Alle vormen van samengestelde toewijzing zijn toegestaan.
a?.b -= M(); // ok
a?.b += M(); // ok
// etc.
- Als het resultaat van de expressie wordt gebruikt, moet het type van de expressie bekend zijn dat het een waardetype of een verwijzingstype is. Dit is consistent met bestaand gedrag voor voorwaardelijke toegang.
class C<T>
{
public T? field;
}
void M1<T>(C<T>? c, T t)
{
(c?.field = t).ToString(); // error: 'T' cannot be made nullable.
c?.field = t; // ok
}
- Expressies voor voorwaardelijke toegang zijn nog steeds geen lvalues en het is nog steeds niet toegestaan om ze te pakken
ref.
M(ref a?.b); // error
- Het is niet toegestaan om opnieuw toe te wijzen aan een voorwaardelijke toegang. De belangrijkste reden hiervoor is dat de enige manier waarop u voorwaardelijk toegang krijgt tot een verw-variabele een verw-veld is en verw-structs niet mogen worden gebruikt in typen null-waarden. Als er in de toekomst een geldig scenario voor een voorwaardelijke verw-toewijzing is opgekomen, kunnen we op dat moment ondersteuning toevoegen.
ref struct RS
{
public ref int b;
}
void M(RS a, ref int x)
{
a?.b = ref x; // error: Operator '?' can't be applied to operand of type 'RS'.
}
- Het is niet mogelijk om bijvoorbeeld toe te wijzen aan voorwaardelijke toegang via de deconstructietoewijzing. We verwachten dat het zeldzaam zal zijn voor mensen om dit te doen, en niet een belangrijk nadeel om dit te doen via meerdere afzonderlijke toewijzingsexpressies in plaats daarvan.
(a?.b, c?.d) = (x, y); // error
- Operatoren voor incrementele/degradatie worden niet ondersteund.
a?.b++; // error
--a?.b; // error
- Deze functie werkt doorgaans niet wanneer de ontvanger van de voorwaardelijke toegang een waardetype is. Dit komt doordat het in een van de volgende twee gevallen valt:
void Case1(MyStruct a)
=> a?.b = c; // a?.b is not allowed when 'a' is of non-nullable value type
void Case2(MyStruct? a)
=> a?.b = c; // `a.Value` is not a variable, so there's no reasonable meaning to define for the assignment
readonly-setter-calls-on-non-variables.md stelt voor dit te ontspannen, in dat geval kunnen we een redelijk gedrag definiëren voor a?.b = c, wanneer a een System.Nullable<T> en b is een eigenschap met een leesbare setter.
Specificatie
De grammatica van de voorwaardelijke toewijzing null wordt als volgt gedefinieerd:
null_conditional_assignment
: null_conditional_member_access assignment_operator expression
: null_conditional_element_access assignment_operator expression
Zie §11.7.7 en §11.7.11 ter referentie.
Wanneer de voorwaardelijke toewijzing null wordt weergegeven in een expressie-instructie, zijn de semantiek ervan als volgt:
-
P?.A = Bis gelijk aanif (P is not null) P.A = B;, behalve dat wordtPslechts eenmaal geëvalueerd. -
P?[A] = Bis gelijk aanif (P is not null) P[A] = B, behalve dat wordtPslechts eenmaal geëvalueerd.
Anders zijn de semantiek als volgt:
-
P?.A = Bis gelijk aan(P is null) ? (T?)null : (P.A = B), waarbijThet resultaattypeP.A = Bis , behalve dat ditPslechts eenmaal wordt geëvalueerd. -
P?[A] = Bis gelijk aan(P is null) ? (T?)null : (P[A] = B), waarbijThet resultaattypeP[A] = Bis , behalve dat ditPslechts eenmaal wordt geëvalueerd.
Implementatie
De grammatica in de standaard komt momenteel niet sterk overeen met het syntaxisontwerp dat in de implementatie wordt gebruikt. We verwachten dat dit het geval blijft nadat deze functie is geïmplementeerd. Het syntaxisontwerp in de implementatie wordt niet verwacht dat deze daadwerkelijk wordt gewijzigd. Alleen de manier waarop deze wordt gebruikt, wordt gewijzigd. Voorbeeld:
graph TD;
subgraph ConditionalAccessExpression
whole[a?.b = c]
end
subgraph
subgraph WhenNotNull
whole-->whenNotNull[".b = c"];
whenNotNull-->.b;
whenNotNull-->eq[=];
whenNotNull-->c;
end
subgraph OperatorToken
whole-->?;
end
subgraph Expression
whole-->a;
end
end
Complexe voorbeelden
class C
{
ref int M() => /*...*/;
}
void M1(C? c)
{
c?.M() = 42; // equivalent to:
if (c is not null)
c.M() = 42;
}
int? M2(C? c)
{
return c?.M() = 42; // equivalent to:
return c is null ? (int?)null : c.M() = 42;
}
M(a?.b?.c = d); // equivalent to:
M(a is null
? null
: (a.b is null
? null
: (a.b.c = d)));
return a?.b = c?.d = e?.f; // equivalent to:
return a?.b = (c?.d = e?.f); // equivalent to:
return a is null
? null
: (a.b = c is null
? null
: (c.d = e is null
? null
: e.f));
}
a?.b ??= c; // equivalent to:
if (a is not null)
{
if (a.b is null)
{
a.b = c;
}
}
return a?.b ??= c; // equivalent to:
return a is null
? null
: a.b is null
? a.b = c
: a.b;
Nadelen
De keuze om de toewijzing binnen de voorwaardelijke toegang te houden, introduceert wat extra werk voor de IDE, die veel codepaden bevat die terug moeten werken van een toewijzing om het toegewezen punt te identificeren.
Alternatieven
We kunnen in plaats daarvan het ?. syntactisch een kind van de =. Hierdoor moet elke verwerking van = expressies zich bewust worden van de conditionaliteit van de rechterkant in aanwezigheid van ?. aan de linkerkant. Het maakt het ook zo dat de structuur van de syntaxis niet zo sterk overeenkomt met de semantiek.
Niet-opgeloste vragen
Ontwerpbijeenkomsten
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-04-27.md#null-conditional-assignment
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-08-31.md#null-conditional-assignment
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-10-26.md#null-conditional-assignment
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-10-28.md#increment-and-decrement-operators-in-null-conditional-access
C# feature specifications