Sdílet prostřednictvím


Podmíněné přiřazení s hodnotou Null

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 jazykového návrhu (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/8677

Shrnutí

Umožňuje podmíněné přiřazení k výrazu v rámci a?.b nebo a?[b].

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();
}

Motivation

V různých prosazovaných otázkách lze najít různé příklady použití. Mezi hlavní motivace patří:

  1. Parita mezi vlastnostmi a Set() metodami
  2. Připojení obslužných rutin událostí v kódu uživatelského rozhraní

Podrobný návrh

  • Pravá strana přiřazení se vyhodnotí pouze v případech, kdy příjemce podmíněného přístupu nemá hodnotu null.
// 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();
  • Jsou povoleny všechny formy složeného přiřazení.
a?.b -= M(); // ok
a?.b += M(); // ok
// etc.
  • Pokud se použije výsledek výrazu, musí být typ výrazu známý jako typ hodnoty nebo odkazový typ. To je konzistentní se stávajícím chováním u podmíněných přístupů.
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
}
  • Výrazy podmíněného přístupu stále nejsou lvalues, a například se k nim stále nesmí použít ref.
M(ref a?.b); // error
  • Není povoleno odkazovat na přiřazení k podmíněnému přístupu. Hlavním důvodem je to, že jediným způsobem, jak byste podmíněně přistupovali k proměnné typu ref, je pole typu ref, a struktury typu ref je zakázáno používat v typech hodnot, které mohou nabývat hodnoty null. Pokud se v budoucnu objevil platný scénář podmíněného přiřazení odkazu, mohli bychom přidat podporu.
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'.
}
  • Podmíněné přístupy není například možné přiřazovat prostřednictvím dekonstrukčního přiřazení. Očekáváme, že lidé to budou dělat jen zřídka, takže místo toho nebude to významnou nevýhodou u více samostatných přiřazovacích výrazů.
(a?.b, c?.d) = (x, y); // error
  • Operátory přírůstku nebo dekrementace se nepodporují.
a?.b++; // error
--a?.b; // error
  • Tato funkce obecně nefunguje, pokud je příjemcem podmíněného přístupu typ hodnoty. Je to proto, že spadá do jednoho z následujících dvou případů:
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 navrhuje uvolnění, v takovém případě bychom mohli definovat rozumné chování pro a?.b = c, když je aSystem.Nullable<T> a b je vlastnost se setterem pouze pro čtení.

Specifikace

Gramatika podmíněného přiřazení null je definována takto:

null_conditional_assignment
    : null_conditional_member_access assignment_operator expression
    : null_conditional_element_access assignment_operator expression

Odkazy najdete v článcích §11.7.7 a §11.7.11 .

Když se podmíněné přiřazení null objeví ve výrazovém příkazu, jeho sémantika je následující:

  • P?.A = B je ekvivalentní hodnotě if (P is not null) P.A = B;, s výjimkou toho, že P se vyhodnotí pouze jednou.
  • P?[A] = B je ekvivalentní hodnotě if (P is not null) P[A] = B, s výjimkou toho, že P se vyhodnotí pouze jednou.

Jinak jsou jeho sémantika následující:

  • P?.A = B je ekvivalentní (P is null) ? (T?)null : (P.A = B), kde T je typ výsledku P.A = B, s výjimkou toho, že P je vyhodnocen pouze jednou.
  • P?[A] = B je ekvivalentní (P is null) ? (T?)null : (P[A] = B), kde T je typ výsledku P[A] = B, s výjimkou toho, že P je vyhodnocen pouze jednou.

Implementace

Gramatika v současné době neodpovídá návrhu syntaxe použitému v implementaci. Očekáváme, že po implementaci této funkce zůstane tento případ zachován. U návrhu syntaxe v implementaci se neočekává, že se skutečně změní pouze způsob, jakým se použije. Například:

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

Složité příklady

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;

Nevýhody

Volba uchovat přiřazení v rámci podmíněného přístupu přináší integrovanému vývojovému prostředí (IDE) další práci, protože obsahuje mnoho kódových cest, které musí pracovat zpětně od přiřazení k identifikaci toho, co je přiřazováno.

Alternatives

Místo toho bychom mohli udělat ?. syntakticky dítětem =. Aby bylo možné jakékoli zpracování = výrazů správně provést, je nutné si uvědomit podmíněnost pravé strany, pokud je na levé straně přítomno ?.. Také to dělá tak, aby struktura syntaxe neodpovídala tak silně sémantice.

Nevyřešené otázky

Designérské schůzky