Aracılığıyla paylaş


Null koşullu atama

Uyarı

Bu makale bir özellik belirtimidir. Belirtim, özelliğin tasarım belgesi olarak görev alır. Önerilen belirtim değişikliklerini ve özelliğin tasarımı ve geliştirilmesi sırasında gereken bilgileri içerir. Bu makaleler, önerilen belirtim değişiklikleri son haline getirilene ve geçerli ECMA belirtimine dahil edilene kadar yayımlanır.

Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar ilgili dil tasarım toplantısı (LDM) notlarında yakalanır.

Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek içinbelirtimleri makalesinde bulabilirsiniz.

Şampiyonluk sorunu: https://github.com/dotnet/csharplang/issues/8677

Özet

Atamanın bir a?.b veya a?[b] ifadesi içinde koşullu olarak gerçekleşmesine izin verir.

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

Motivasyon

Desteklenen sorunda çeşitli ilgi çekici kullanım örnekleri bulunabilir. Başlıca motivasyonlar şunlardır:

  1. Özellikler ve Set() yöntemler arasındaki eşlik.
  2. Kullanıcı arabirimi koduna olay işleyicileri ekleme.

Ayrıntılı tasarım

  • Atamanın sağ tarafı yalnızca koşullu erişimin alıcısı null olmadığında değerlendirilir.
// 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();
  • Tüm bileşik atama biçimlerine izin verilir.
a?.b -= M(); // ok
a?.b += M(); // ok
// etc.
  • İfadenin sonucu kullanılırsa, ifadenin türünün bir değer türünde veya başvuru türünde olduğu bilinmelidir. Bu, koşullu erişimlerdeki mevcut davranışlarla tutarlıdır.
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
}
  • Koşullu erişim ifadeleri hala lvalues değildir ve yine de örneğin bunlara ref uygulamak izni verilmez.
M(ref a?.b); // error
  • Koşullu erişime yeni bir referans atamak yasaktır. Bunun temel nedeni, bir başvuru değişkenine koşullu olarak erişmenin tek yolunun bir başvuru alanı olması ve başvuru yapılarının null atanabilir değer türlerinde kullanılmasının yasak olmasıdır. İleride koşullu referans ataması için geçerli bir senaryo ortaya çıkarsa, o zaman destek ekleyebiliriz.
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'.
}
  • Yapısöküm ataması yoluyla koşullu erişimlere atama yapmak mümkün değildir. İnsanların bunu yapmak istemelerinin nadir olacağını ve bunun yerine birden çok ayrı atama ifadesi üzerinde bunu yapması gerektiğinin önemli bir dezavantajı olmadığını tahmin ediyoruz.
(a?.b, c?.d) = (x, y); // error
a?.b++; // error
--a?.b; // error
  • Koşullu erişimin alıcısı bir değer türü olduğunda bu özellik genellikle çalışmaz. Bunun nedeni, aşağıdaki iki durumdan birine denk gelecek olmasıdır:
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 bunu gevşetmeyi önerir, bu durumda a?.b = c, a, ve System.Nullable<T> salt okunur ayarlayıcıya sahip bir özellik olduğunda b için makul bir davranış tanımlayabiliriz.

Spesifikasyon

Null koşullu atama dil bilgisi aşağıdaki gibi tanımlanır:

null_conditional_assignment
    : null_conditional_member_access assignment_operator expression
    : null_conditional_element_access assignment_operator expression

Başvuru için bkz §11.7.7 ve §11.7.11.

Null koşullu atama bir ifade-deyiminde göründüğünde, semantiği aşağıdaki gibidir:

  • P?.A = B ile eşdeğerdir if (P is not null) P.A = B;, ancak yalnızca P bir kez değerlendirilir.
  • P?[A] = B ile eşdeğerdir if (P is not null) P[A] = B, ancak yalnızca P bir kez değerlendirilir.

Aksi takdirde semantiği aşağıdaki gibidir:

  • P?.A = B, (P is null) ? (T?)null : (P.A = B) ile eşdeğerdir; burada TP.A = B'ün sonuç türüdür ancak P yalnızca bir kez değerlendirilir.
  • P?[A] = B, (P is null) ? (T?)null : (P[A] = B) ile eşdeğerdir; burada TP[A] = B'ün sonuç türüdür ancak P yalnızca bir kez değerlendirilir.

Uygulama

Standarttaki dil bilgisi şu anda uygulamada kullanılan söz dizimi tasarımına kesin olarak karşılık gelmiyor. Bu özellik uygulandıktan sonra da böyle kalmasını bekliyoruz. Uygulamadaki söz dizimi tasarımının gerçekten değişmesi beklenmez; yalnızca kullanım şekli değişir. Örneğin:

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

Karmaşık örnekler

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;

Dezavantaj -ları

Atamayı koşullu erişimde tutma seçeneği, atamadan atanan öğeyi tanımlamak için geriye doğru çalışması gereken birçok kod yolunun bulunduğu IDE için bazı ek çalışmalar sağlar.

Alternatifler

Bunun yerine, ?. öğesini dizimsel olarak ='ün alt öğesi yapabiliriz. Bu, = ifadelerinin işlenmesi sırasında, solda ?. bulunduğunda sağ tarafın koşulluluğundan haberdar olunmasını gerektirir. Ayrıca söz diziminin yapısının semantikle tam olarak uyuşmamasını sağlar.

Çözülmemiş sorular

Tasarım toplantıları