Apakah yang ?[]? sintaks dalam C #?

85

Sementara aku mempelajari delegasi yang merupakan sebenarnya merupakan kelas abstrak dalam Delegate.cs, saya melihat metode berikut di mana saya tidak mengerti

  • Mengapa nilai pengembalian menggunakan ?meskipun itu sudah tipe referensi ( kelas )
  • ?[]? artinya pada parameter

Bisakah Anda jelaskan?

public static Delegate? Combine(params Delegate?[]? delegates)
{
    if (delegates == null || delegates.Length == 0)
        return null;

    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}
snr - Pasang kembali Monica
sumber
23
Bukankah itu array nullable yang bisa berisi nilai nullable?
Cid
3
c # 8, kini Anda dapat menentukan bahwa variabel objek tidak boleh nol. Jika Anda flip bahwa bendera compiler, Anda harus menentukan setiap variabel yang yang boleh nol.
Jeremy Lakeman

Jawaban:

66

Penjelasan langkah demi langkah:

params Delegate?[] delegates - Ini adalah array yang dapat dibatalkan Delegate

params Delegate?[]? delegates - Seluruh array dapat dibatalkan

Karena setiap parameter adalah tipe Delegate?dan Anda mengembalikan indeks Delegate?[]?array, maka masuk akal bahwa tipe kembali adalah Delegate?kompiler yang akan mengembalikan kesalahan seolah-olah Anda sedang returing dan intdari metode yang mengembalikan string.

Anda dapat mengubah, misalnya, kode Anda untuk mengembalikan Delegatetipe seperti ini:

public static Delegate Combine(params Delegate?[]? delegates)
{
    Delegate defaulDelegate = // someDelegate here
    if (delegates == null || delegates.Length == 0)
        return defaulDelegate;

    Delegate d = delegates[0] ?? defaulDelegate;
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}
Athanasios Kataras
sumber
4
bagaimana dengan pertanyaan pertama saya? Mengapa nilai pengembalian menggunakan ?meskipun itu sudah referensi (kelas) tipe
snr - Reinstate Monica
2
Karena Anda akan kembali, delegasi yang mana? Tipe. Dan juga nol atau Delegasi tergantung pada parameter
Athanasios Kataras
2
@ snr Nilai kembali menggunakan ?untuk alasan yang persis sama mengapa nilai argumen menggunakan ?. Jika Anda memahami yang terakhir, Anda secara otomatis memahami yang pertama. Karena metode ini dapat mengembalikan nol , seperti parameter dapat menerima nol . Artinya, keduanya mungkin mengandung null .
GSerg
2
Saya menyentuhnya. Saya baru saja menjawabnya sebagai bagian kedua dengan contoh. Since each parameter is of the type Delegate? and you return an index of the Delegate?[]? array, then it makes sense that the return type is Delegate?
Athanasios Kataras
2
@ snr: Mengapa nilai pengembalian menggunakan? meskipun itu sudah referensi (kelas) tipe (1) Ini mungkin kebiasaan atau sisa refactoring jika struct juga digunakan dalam kode yang sama (2) Dalam C # 8 jenis referensi dapat dianulir untuk secara inheren nullable (sehingga membutuhkan nullability eksplisit ( 3) Foo?memiliki HasValueproperti yang rapi , sedangkan Fooperlu == nulldiperiksa (4) Mengkomunikasikan kepada pengembang yang membaca metode tanda tangan bahwa null adalah suatu kemungkinan, yang diharapkan, dan hasil yang benar. (5) Kode tersebut kelihatannya ditulis oleh seseorang yang sangat menyukai nullability dan bersikap eksplisit tentang hal itu
Flater
25

Jenis Referensi Nullable baru di C # 8.0, mereka tidak ada sebelumnya.

Ini masalah dokumentasi, dan bagaimana peringatan pada waktu kompilasi dihasilkan.

Pengecualian "objek tidak diatur ke turunan objek" adalah pengecualian umum. Tapi ini adalah pengecualian runtime, itu sebagian dapat ditemukan pada waktu kompilasi.

Untuk pelanggan tetap, Delegate dAnda selalu dapat menelepon

 d.Invoke();

artinya, Anda dapat kode itu, pada waktu kompilasi tidak ada yang akan terjadi. Itu dapat menimbulkan pengecualian saat runtime.

Sedangkan untuk yang baru Delegate? pKode ini

 p.Invoke();

akan menghasilkan peringatan kompiler. CS8602: Dereference of a possibly null reference kecuali jika Anda menulis:

 p?.Invoke();

apa artinya, panggil saja jika tidak nol.

Jadi, Anda mendokumentasikan variabel mungkin berisi null atau tidak. Ini memunculkan peringatan sebelumnya dan dapat menghindari beberapa tes untuk null. Apa yang Anda miliki untuk int dan int? Anda tahu pasti, satu bukan nol - dan Anda tahu cara mengonversi satu ke yang lain.

Holger
sumber
dengan peringatan yang Anda tidak tahu pasti itu Delegatebukan nol. Anda hanya berpura-pura yakin (yang cukup baik dalam kebanyakan kasus).
Tim Pohlmann
@TimPohlmann Serta int i = null tidak mungkin, juga string s = null tidak mungkin (dalam C # 8 dengan perubahan yang melanggar ini). Jadi itu lebih dari berpura-pura sesuatu. Untuk kompatibilitas mundur ini diturunkan ke peringatan. Jika Anda meningkatkan peringatan ke kesalahan, Anda tahu pasti itu bukan nol.
Holger
4
Terakhir kali saya memeriksa NRT bukan 100% bukti peluru. Ada beberapa kasus tepi tertentu yang tidak dapat dideteksi oleh kompiler.
Tim Pohlmann
Ya, tapi ini sama dengan peringatan "variabel tidak diinisialisasi" atau "tidak semua jalur kode yang mengembalikan nilai". Itu semua 100% fitur waktu kompilasi, tanpa deteksi saat runtime. Tidak seperti int ?, saya pikir mereka tidak menambahkan memori ekstra untuk menyimpan informasi tambahan. Jadi katakanlah itu dapat diandalkan seperti intellisense. Cukup bagus.
Holger
1
Jika Anda memilikinya int, tidak akan pernah menjadi nol. Jika Anda memiliki Delegateitu bisa nol (karena berbagai alasan, misalnya refleksi). Biasanya aman untuk mengasumsikan bahwa Delegate(dalam C # 8 dengan NRT diaktifkan) bukan nol, tetapi Anda tidak pernah tahu pasti (di mana untuk intkami tahu pasti).
Tim Pohlmann
5

Dalam C # 8 seseorang harus secara eksplisit menandai jenis referensi sebagai nullable.

Secara default, tipe-tipe itu tidak dapat mengandung null, agak mirip dengan tipe nilai. Meskipun ini tidak mengubah cara kerja di bawah tenda, pemeriksa tipe akan meminta Anda untuk melakukan ini secara manual.

Kode yang diberikan dihidupkan kembali untuk bekerja dengan C # 8, tetapi tidak mendapat manfaat dari fitur baru ini.

public static Delegate? Combine(params Delegate?[]? delegates)
{
    // ...[]? delegates - is not null-safe, so check for null and emptiness
    if (delegates == null || delegates.Length == 0)
        return null;

    // Delegate? d - is not null-safe too
    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

Berikut adalah contoh kode yang diperbarui (tidak berfungsi, hanya gagasan) yang memanfaatkan fitur ini. Ini menyelamatkan kami dari pemeriksaan nol dan sedikit menyederhanakan metode ini.

public static Delegate? Combine(params Delegate[] delegates)
{
    // `...[] delegates` - is null-safe, so just check if array is empty
    if (delegates.Length == 0) return null;

    // `d` - is null-safe too, since we know for sure `delegates` is both not null and not empty
    Delegate d = delegates[0];

    for (int i = 1; i < delegates.Length; i++)
        // then here is a problem if `Combine` returns nullable
        // probably, we can add some null-checks here OR mark `d` as nullable
        d = Combine(d, delegates[i]);

    return d;
}
amankkg
sumber
2
those types are not able to contain null, perhatikan bahwa itu menghasilkan peringatan kompiler , bukan kesalahan. Kode akan berjalan dengan baik (meskipun mungkin menghasilkan NullReferenceExceptions). Ini dilakukan dengan mengingat kompatibilitas ke belakang.
JAD