Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Nota
Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.
Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut tertuangkan dalam catatan rapat terkait desain bahasa (LDM) .
Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .
Edisi unggulan: https://github.com/dotnet/csharplang/issues/8677
Ringkasan
Mengizinkan penugasan terjadi secara kondisional dalam ekspresi a?.b atau 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();
}
Motivasi
Berbagai kasus penggunaan yang memotivasi dapat ditemukan dalam masalah yang diperjuangkan. Motivasi utama meliputi:
- Paritas antara properti dan
Set()metode. - Melampirkan penanganan aktivitas dalam kode UI.
Desain terperinci
- Sisi kanan penugasan hanya dievaluasi ketika penerima akses kondisional tidak bernilai 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();
- Semua bentuk penugasan gabungan diizinkan.
a?.b -= M(); // ok
a?.b += M(); // ok
// etc.
- Jika hasil ekspresi digunakan, jenis ekspresi harus diketahui dari jenis nilai atau jenis referensi. Ini konsisten dengan perilaku yang ada pada akses bersyarat.
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
}
- Ekspresi akses bersyarat masih belum lvalues, dan masih belum diizinkan untuk misalnya, mengambil
refke ekspresi tersebut.
M(ref a?.b); // error
- Tidak diperbolehkan untuk mengubah referensi menjadi akses kondisional. Alasan utama untuk ini adalah bahwa satu-satunya cara Anda akan secara kondisional mengakses variabel ref adalah bidang ref, dan struktur ref dilarang digunakan dalam jenis nilai nullable. Jika skenario yang valid untuk penetapan ref kondisional muncul di masa mendatang, kami dapat menambahkan dukungan pada saat itu.
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'.
}
- Tidak dimungkinkan untuk misalnya menetapkan ke akses bersyarat melalui penugasan dekonstruksi. Kami mengantisipasinya akan jarang orang ingin melakukan ini, dan bukan kelemahan yang signifikan untuk perlu melakukannya melalui beberapa ekspresi penugasan terpisah sebagai gantinya.
(a?.b, c?.d) = (x, y); // error
- Operator kenaikan/penurunan tidak didukung.
a?.b++; // error
--a?.b; // error
- Fitur ini umumnya tidak berfungsi ketika penerima akses bersyarat adalah jenis nilai. Ini karena akan jatuh ke dalam salah satu dari dua kasus berikut:
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 mengusulkan untuk melonggarkan ini, dalam hal ini kita dapat menentukan perilaku yang wajar untuk a?.b = c, ketika a adalah System.Nullable<T> dan b merupakan properti dengan readonly setter.
Spesifikasi
Tata bahasa penetapan kondisi null didefinisikan sebagai berikut:
null_conditional_assignment
: null_conditional_member_access assignment_operator expression
: null_conditional_element_access assignment_operator expression
Lihat §11.7.7 dan §11.7.11 untuk referensi.
Saat penetapan kondisi null muncul dalam pernyataan ekspresi, semantiknya adalah sebagai berikut:
-
P?.A = Bsetara denganif (P is not null) P.A = B;, kecuali yangPhanya dievaluasi sekali. -
P?[A] = Bsetara denganif (P is not null) P[A] = B, kecuali yangPhanya dievaluasi sekali.
Jika tidak, semantiknya adalah sebagai berikut:
-
P?.A = Bsetara dengan(P is null) ? (T?)null : (P.A = B), di manaTadalah jenis hasil ,P.A = Bkecuali yangPhanya dievaluasi sekali. -
P?[A] = Bsetara dengan(P is null) ? (T?)null : (P[A] = B), di manaTadalah jenis hasil ,P[A] = Bkecuali yangPhanya dievaluasi sekali.
Pelaksanaan
Tata bahasa dalam standar saat ini tidak sangat sesuai dengan desain sintaks yang digunakan dalam implementasi. Kami berharap bahwa tetap terjadi setelah fitur ini diterapkan. Desain sintaks dalam implementasi tidak diharapkan benar-benar berubah--hanya cara yang digunakan yang akan berubah. Contohnya:
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
Contoh kompleks
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;
Kekurangan
Pilihan untuk mempertahankan penugasan dalam akses bersyarat memperkenalkan beberapa pekerjaan tambahan untuk IDE, yang memiliki banyak jalur kode yang perlu bekerja mundur dari penugasan untuk mengidentifikasi hal yang ditugaskan.
Alternatif
Kita bisa menjadikan ?. secara sintis sebagai anak dari =. Ini membuat pengelolaan ekspresi = perlu menyadari kondisionalitas sisi kanan di hadapan ?. di sisi kiri. Ini juga menyebabkan struktur sintaks tidak terlalu sesuai dengan semantik.
Pertanyaan yang belum terselesaikan
Rapat desain
- 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