Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Замечание
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Этот документ включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия отражены в соответствующих заметках с заседания по дизайну языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Вопрос чемпиона: https://github.com/dotnet/csharplang/issues/8677
Сводка
Разрешает назначение условно в пределах a?.b или 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();
}
Мотивация
Различные мотивирующие варианты использования можно найти в проблеме с чемпионом. К основным мотивациям относятся:
- Четность между свойствами и
Set()методами. - Присоединение обработчиков событий в коде пользовательского интерфейса.
Подробный дизайн
- Правая сторона назначения вычисляется только в том случае, если получатель условного доступа не имеет значения 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();
- Разрешены все формы составного назначения.
a?.b -= M(); // ok
a?.b += M(); // ok
// etc.
- Если используется результат выражения, тип выражения должен быть известен типом значения или ссылочным типом. Это согласуется с существующим поведением условного доступа.
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
}
- Выражения условного доступа по-прежнему не являются значениями lvalue, и они по-прежнему не допускаются, например принимать
refих.
M(ref a?.b); // error
- Невозможно назначить ссылку условному доступу. Основной причиной этого является то, что единственным способом условного доступа к переменной ref является поле ссылок, и ссылочные структуры запрещены использовать в типах значений, допускающих значение NULL. Если допустимый сценарий условного назначения ссылок появился в будущем, мы могли бы добавить поддержку в то время.
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'.
}
- Например, невозможно назначить условный доступ с помощью деконструкции. Мы ожидаем, что это будет редко для людей, которые хотят сделать это, и не значительный недостаток, чтобы сделать это над несколькими отдельными выражениями назначения вместо этого.
(a?.b, c?.d) = (x, y); // error
- Операторы добавочного и декремента не поддерживаются.
a?.b++; // error
--a?.b; // error
- Эта функция обычно не работает, если получатель условного доступа является типом значения. Это связано с тем, что это приведет к одному из следующих двух случаев:
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 предлагает расслабиться, в этом случае мы могли бы определить разумное поведение a?.b = cдля , когда a является и b является System.Nullable<T> свойством с методом чтения.
Specification
Грамматика условного назначения null определяется следующим образом:
null_conditional_assignment
: null_conditional_member_access assignment_operator expression
: null_conditional_element_access assignment_operator expression
Дополнительные сведения см. в разделе "11.7.7" и "11.7.11".
Если условное назначение null отображается в операторе expression-statement, его семантика выглядит следующим образом:
-
P?.A = Bэквивалентенif (P is not null) P.A = B;, за исключением того, чтоPвычисляется только один раз. -
P?[A] = Bэквивалентенif (P is not null) P[A] = B, за исключением того, чтоPвычисляется только один раз.
В противном случае его семантика выглядит следующим образом:
-
P?.A = Bэквивалентно(P is null) ? (T?)null : (P.A = B), гдеTявляется типP.A = Bрезультата, за исключением того, чтоPвычисляется только один раз. -
P?[A] = Bэквивалентно(P is null) ? (T?)null : (P[A] = B), гдеTявляется типP[A] = Bрезультата, за исключением того, чтоPвычисляется только один раз.
Implementation
Грамматика в стандарте в настоящее время не соответствует строго синтаксису, используемому в реализации. Мы ожидаем, что останется дело после реализации этой функции. Разработка синтаксиса в реализации на самом деле не изменится только тем способом, который он будет использоваться. Рассмотрим пример.
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
Сложные примеры
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;
Недостатки
Выбор для сохранения назначения в условном доступе представляет некоторую дополнительную работу для интегрированной среды разработки, которая содержит множество путей кода, которые должны работать назад от назначения к идентификации назначенной вещи.
Alternatives
Мы могли бы вместо этого сделать ?. синтаксически дочерним элементом =. Это делает его таким образом, чтобы любая обработка = выражений должна быть осведомлена об условности правой стороны в присутствии ?. слева. Он также делает его так, чтобы структура синтаксиса не соответствовала строго семантике.
Неразрешенные вопросы
Проектирование собраний
- 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