Apakah tidak masuk akal mengharapkan Any () * tidak * melempar pengecualian referensi nol?

41

Ketika Anda membuat metode ekstensi, Anda dapat, tentu saja, memanggilnya null. Tapi, tidak seperti pemanggilan metode instan, memanggilnya dengan null tidak harus membuang NullReferenceException-> Anda harus memeriksa dan membuangnya secara manual.

Untuk implementasi metode ekstensi Linq, Any()Microsoft memutuskan bahwa mereka harus melempar ArgumentNullException( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).

Itu membuatku kesal harus menulis if( myCollection != null && myCollection.Any() )

Apakah saya salah, sebagai klien dari kode ini, untuk berharap bahwa misalnya ((int[])null).Any()harus kembali false?

lanjutkan ini
sumber
41
di C # 6, Anda dapat menyederhanakan cek Anda jika (myCollection? .Any () == true)
17 dari 26
5
Bahkan dalam F #, yang tidak benar-benar menggunakan nulls kecuali untuk interoperabilitas dengan bahasa .NET lainnya, null |> Seq.isEmptymelempar System.ArgumentNullException: Value cannot be null. Harapannya tampaknya bahwa Anda tidak akan melewati nilai yang tidak ditentukan untuk sesuatu yang diharapkan ada, jadi itu masih merupakan pengecualian ketika Anda memiliki nol. Jika ini masalah inisialisasi, saya akan mulai dengan urutan kosong, bukan null.
Aaron M. Eshbach
21
Itu sebabnya Anda tidak boleh kembali nullketika berhadapan dengan koleksi tetapi gunakan koleksi kosong dalam kasus tersebut.
Bakuriu
31
Nah mengapa itu nol? Anda tidak ingin null dihitung sebagai kosong, karena tidak kosong, itu sesuatu yang sama sekali berbeda. Mungkin Anda ingin itu dihitung sebagai kosong dalam kasus Anda, tetapi menambahkan hal semacam itu ke bahasa mengarah ke bug.
user253751
22
Saya harus menunjukkan bahwa setiap metode LINQ menggunakan argumen nol. Anyhanya konsisten.
Sebastian Redl

Jawaban:

157

Saya punya tas dengan lima kentang di dalamnya. Apakah ada .Any()kentang di dalam tas?

" Ya ," katamu.<= true

Saya mengambil semua kentang dan memakannya. Apakah ada .Any()kentang di dalam tas?

" Tidak ," katamu.<= false

Aku benar-benar membakar kantong itu di api. Apakah ada .Any()kentang di dalam tas sekarang?

" Tidak ada tas ."<= ArgumentNullException

Dan Wilson
sumber
3
Tapi kosong, tidak ada kentang di dalam tas? Salah menyiratkan Salah ... (Saya tidak setuju, hanya perspektif kedua).
D. Ben Knoble
6
lebih praktisnya, seseorang memberi Anda sebuah kotak tertutup dari sumber yang tidak dikenal. Apakah tas di dalam kotak berisi kentang? Saya hanya tertarik pada 2 skenario - A) ada tas di kotak yang berisi kentang, atau B) tidak ada kentang dan / atau tidak ada tas di dalam kotak. Semantik, ya, ada perbedaan antara nol dan kosong, tetapi 99% dari waktu saya tidak peduli.
dave thieben
6
Anda makan lima kentang mentah ?? Segera cari bantuan medis.
Oly
3
@Oly Dan memasaknya di api, sebelum makan dan menggunakan api itu untuk membakar tas.
Ismael Miguel
3
@ D.BenKnoble: Tanpa melihat bagasi mobil saya, apakah Anda bersedia untuk bersaksi bahwa tidak ada mayat di bagasi mobil saya? Tidak? Apa perbedaan antara memeriksa bagasi dan tidak menemukan apa pun, atau tidak memeriksa bagasi? Tidak bisakah Anda bersaksi sekarang, karena Anda akan melihat jumlah tubuh yang sama persis dalam kedua kasus? ... Itulah bedanya. Anda tidak dapat menjamin sesuatu jika Anda tidak dapat benar-benar mengkonfirmasi itu di tempat pertama. Anda berasumsi bahwa keduanya setara, tetapi itu tidak diberikan. Dalam beberapa kasus, mungkin ada perbedaan penting antara keduanya.
Flater
52

Pertama, tampaknya kode sumber itu akan melempar ArgumentNullException, bukan NullReferenceException.

Karena itu, dalam banyak kasus Anda sudah tahu bahwa koleksi Anda bukan nol, karena kode ini hanya dipanggil dari kode yang tahu bahwa koleksi tersebut sudah ada, sehingga Anda tidak perlu sering meletakkan cek nol di sana. Tetapi jika Anda tidak tahu bahwa itu ada, maka masuk akal untuk memeriksa sebelum memanggilnya Any().

Apakah saya salah, sebagai klien dari kode ini, untuk berharap bahwa misalnya ((int[])null).Any()harus kembali false?

Iya nih. Pertanyaan yang Any()dijawab adalah "apakah koleksi ini mengandung elemen?" Jika koleksi ini tidak ada, maka pertanyaannya sendiri tidak masuk akal; itu tidak dapat mengandung atau tidak mengandung apa pun, karena itu tidak ada.

Mason Wheeler
sumber
18
@ChrisWohlert: Intuisi Anda membuat saya penasaran. Apakah Anda juga berpikir bahwa secara semantik, orang yang tidak ada adalah orang kosong, yang namanya adalah string kosong dan umurnya nol dan seterusnya? Dan menurut Anda apakah set yang berisi nullsetara dengan set yang berisi set kosong (dan sebaliknya)?
ruakh
17
@ChrisWohlert: Jelas satu set dapat berisi set kosong; tetapi Anda tampaknya percaya itu { null }dan { {} }harus setara. Saya menemukan itu menarik; Saya tidak bisa menghubungkannya sama sekali.
ruakh
9
Haruskah .Addpada nullkoleksi entah bagaimana membuat koleksi untuk kita juga?
Matius
13
@ChrisWohlert "A adalah set kosong. B adalah set yang mengandung semangka. Apakah A kosong? Ya. Apakah B kosong? Tidak. Apakah C kosong? Uhhh ..."
user253751
16
Poin pedantic: Bukan itu masalahnya, dalam teori himpunan, himpunan yang tidak ada adalah himpunan kosong. Set kosong ada, dan set non-ada bukan (dan memang, tidak apa-apa, karena tidak ada).
James_pic
22

Null berarti informasi yang hilang, bukan elemen.

Anda mungkin mempertimbangkan untuk menghindari null secara lebih luas - misalnya, gunakan salah satu enumerables kosong bawaan untuk mewakili koleksi tanpa elemen, bukan nol.

Jika Anda mengembalikan nol dalam beberapa keadaan, Anda dapat mengubahnya untuk mengembalikan koleksi kosong. (Kalau tidak, jika Anda menemukan null dikembalikan oleh metode perpustakaan (bukan milik Anda), itu disayangkan, dan saya akan membungkusnya untuk dinormalisasi.)

Lihat juga https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty

Erik Eidt
sumber
1
Itu nulltidak berarti tidak ada unsur yang merupakan masalah menggunakan konvensi yang masuk akal. Hanya berpikir tentang OLESTRINGS asli, di mana itu benar-benar tidak berarti itu.
Deduplicator
1
@Dupuplikator, apakah Anda sedang berbicara tentang Microsoft Object Linking and Embedding perchance? Jika Anda ingat bahwa OLE berasal dari tahun 90-an! Terlihat lebih saat ini, banyak bahasa modern (C #, Java, Swift) menyediakan fasilitas untuk menghilangkan / menghapus penggunaan null.
Erik Eidt
2
@ErikEidt Mereka melakukan itu, sebagian, karena nulltidak berarti "informasi yang hilang". nulltidak memiliki interpretasi yang bermakna tunggal. Sebaliknya, itu adalah masalah bagaimana Anda memperlakukannya. nullkadang-kadang digunakan untuk "informasi yang hilang", tetapi juga untuk "tidak ada elemen", dan juga untuk "kesalahan terjadi" atau "tidak diinisialisasi" atau "informasi yang bertentangan" atau sesuatu yang sewenang-wenang dan ad-hoc.
Derek Elkins
-1. Ini adalah keputusan yang dibuat oleh pengembang, bukan oleh teori abstrak. nullsangat sering digunakan untuk berarti "tidak ada data." Terkadang itu masuk akal. Lain waktu, tidak.
jpmc26
13

Selain sintaksis bersyarat nol , ada teknik lain untuk mengatasi masalah ini: jangan biarkan variabel Anda tetap ada null.

Pertimbangkan fungsi yang menerima koleksi sebagai parameter. Jika untuk tujuan fungsi, nulldan kosong adalah setara, Anda dapat memastikan bahwa itu tidak pernah berisi nulldi awal:

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

Anda dapat melakukan hal yang sama ketika mengambil koleksi dari beberapa metode lain:

var words = GetWords() ?? Enumerable.Empty<string>();

(Perhatikan bahwa dalam kasus di mana Anda memiliki kontrol atas fungsi seperti GetWordsdan nullsetara dengan koleksi kosong, lebih baik mengembalikan koleksi kosong di tempat pertama.)

Sekarang Anda dapat melakukan operasi apa pun keinginan Anda pada koleksi. Ini sangat membantu jika Anda perlu melakukan banyak operasi yang akan gagal saat pengumpulan null, dan dalam kasus di mana Anda mendapatkan hasil yang sama dengan mengulang atau meminta kuota yang kosong, itu akan memungkinkan untuk menghilangkan ifkondisi sepenuhnya.

jpmc26
sumber
12

Apakah saya salah, sebagai klien dari kode ini, untuk berharap bahwa eg ((int []) null). Any () harus mengembalikan false?

Ya, hanya karena Anda berada di C # dan perilaku itu didefinisikan dan didokumentasikan dengan baik.

Jika Anda membuat perpustakaan Anda sendiri, atau jika Anda menggunakan bahasa yang berbeda dengan budaya pengecualian yang berbeda maka akan lebih masuk akal untuk mengharapkan false.

Secara pribadi saya merasa seolah-olah return false adalah pendekatan yang lebih aman yang membuat program Anda lebih kuat, tetapi setidaknya bisa diperdebatkan.

Telastyn
sumber
15
'Kekokohan' itu sering merupakan ilusi - jika kode yang menghasilkan array tiba-tiba mulai menghasilkan NULL secara tidak terduga karena bug atau masalah lain, kode 'kuat' Anda akan menyembunyikan masalahnya. Itu perilaku yang tepat di kali, tetapi bukan default yang aman.
GoatInTheMachine
1
@GoatInTheMachine - Saya tidak setuju tentang hal itu sebagai default yang aman, tetapi Anda benar bahwa itu memang menyembunyikan masalah dan perilaku seperti itu kadang-kadang tidak diinginkan. Dalam pengalaman saya, menyembunyikan masalah (atau mempercayai pengujian unit untuk menangkap masalah kode lain) lebih baik daripada menabrak aplikasi Anda dengan melemparkan pengecualian yang tidak terduga. Maksud saya benar-benar - jika kode panggilan cukup rusak untuk mengirim nol, apa peluang mereka menangani pengecualian dengan benar?
Telastyn
10
Jika ada yang rusak, apakah itu kode saya, rekan lain atau klien, saya lebih suka kode gagal segera dan orang tahu tentang hal itu daripada dilakukan dalam keadaan yang berpotensi tidak konsisten untuk jumlah waktu yang tidak ditentukan. Mampu melakukan ini tentu saja kadang-kadang merupakan kemewahan dan tidak selalu tepat tetapi itulah pendekatan yang saya sukai.
GoatInTheMachine
1
@GoatInTheMachine - Saya setuju untuk banyak hal, tetapi koleksi cenderung berbeda. Memperlakukan null sebagai koleksi kosong bukanlah perilaku yang sangat tidak konsisten atau mengejutkan.
Telastyn
8
Bayangkan Anda memiliki larik yang terisi dari dokumen JSON yang diterima melalui permintaan web, dan dalam produksi, salah satu klien API Anda tiba-tiba memiliki masalah ketika mereka mengirim sebagian JSON yang tidak valid yang menyebabkan Anda membatalkan deserisasi larik sebagai NULL, apakah Anda lebih suka: kode, paling buruk, melemparkan pengecualian referensi NULL saat permintaan buruk pertama kali datang, yang akan Anda lihat di log dan kemudian segera memberi tahu klien untuk memperbaiki permintaan yang rusak, ATAU kode Anda mengatakan "oh, array kosong, tidak ada melakukan!" dan diam-diam menulis 'keranjang pembelian tidak memiliki barang' ke db selama beberapa minggu?
GoatInTheMachine
10

Jika pemeriksaan nol yang diulang mengganggu Anda, Anda bisa membuat metode ekstensi 'IsNullOrEmpty ()' Anda sendiri, untuk mencerminkan metode String dengan nama itu, dan membungkus kedua pemeriksaan nol dan panggilan .Any () menjadi satu panggilan.

Kalau tidak, solusinya, yang disebutkan oleh @ 17 dari 26 dalam komentar di bawah pertanyaan Anda, juga lebih pendek dari metode 'standar', dan cukup jelas bagi siapa pun yang akrab dengan sintaks null-kondisional baru.

if(myCollection?.Any() == true)
Theo Brinkman
sumber
4
Anda juga bisa menggunakan ?? falsebukan == true. Ini akan tampak lebih eksplisit (bersih) bagi saya karena hanya menangani kasus null, dan tidak benar-benar bertele-tele lagi. Ditambah ==cek untuk Boolean biasanya memicu refleks muntah saya. =)
jpmc26
2
@ jpmc26: Saya tidak tahu banyak tentang C #. Kenapa tidak myCollection?.Any()cukup?
Eric Duminil
6
@EricDuminil Karena cara operator null-kondisional bekerja, myCollection?.Any()secara efektif mengembalikan Boolean yang dapat dibatalkan daripada Boolean biasa (yaitu, bool? Bukan bool). Tidak ada konversi implisit dari bool? untuk bool, dan itu bool yang kita butuhkan dalam contoh khusus ini (untuk menguji sebagai bagian dari ifkondisi). Oleh karena itu, kita harus secara eksplisit membandingkan dengan trueatau menggunakan operator penggabungan nol (??).
Steven Rands
@ jpmc26 ?? false lebih sulit dibaca daripada myCollection? .Any () == true. Terlepas dari refleks muntah Anda, == true adalah apa yang Anda coba uji secara implisit. ?? false hanya menambahkan suara tambahan dan Anda masih perlu melakukan evaluasi di kepala Anda tentang apa yang terjadi jika null, bahwa null == benar akan gagal, hampir tanpa pembaca bahkan harus menyadari ?..
Ryan The Leach
2
@RyanTheLeach Saya harus berpikir lebih keras tentang apakah ==akan benar-benar bekerja. Saya sudah tahu apa yang terjadi dengan Boolean secara intuitif padahal hanya bisa trueatau false; Aku bahkan tidak perlu memikirkannya. Tapi nullikut campur, dan sekarang saya harus mencari tahu apakah itu ==benar. ??alih-alih hanya menghilangkan nullkasing, mengurangi kembali ke truedan false, yang sudah Anda mengerti segera. Adapun refleks muntah saya, ketika saya pertama kali melihatnya, insting langsung saya adalah menghapusnya karena biasanya tidak berguna, yang Anda tidak ingin terjadi.
jpmc26
8

Apakah saya salah, sebagai klien dari kode ini, untuk berharap bahwa eg ((int []) null). Any () harus mengembalikan false?

Jika Anda bertanya-tanya tentang harapan, Anda harus memikirkan niat.

null berarti sesuatu yang sangat berbeda Enumerable.Empty<T>

Seperti yang disebutkan dalam jawaban Erik Eidt , ada perbedaan makna antara nulldan koleksi kosong.

Pertama mari kita melihat bagaimana mereka seharusnya digunakan.

Buku Pedoman Kerangka Kerja Desain: Konvensi, Idiom, dan Pola untuk Reusable .NET Library, Edisi ke-2 yang ditulis oleh arsitek Microsoft Krzysztof Cwalina dan Brad Abrams menyatakan praktik terbaik berikut ini:

X JANGAN mengembalikan nilai nol dari properti koleksi atau dari metode pengembalian koleksi. Kembalikan koleksi kosong atau array kosong sebagai gantinya.

Pertimbangkan memanggil metode yang pada akhirnya mendapatkan data dari database: Jika Anda menerima array kosong atau Enumerable.Empty<T>ini berarti ruang sampel Anda kosong, yaitu hasil Anda adalah set kosong . Menerima nulldalam konteks ini, bagaimanapun, akan menandakan keadaan kesalahan .

Dalam pemikiran yang sama dengan analogi kentang Dan Wilson , masuk akal untuk mengajukan pertanyaan tentang data Anda meskipun itu adalah set kosong . Tapi itu jauh lebih masuk akal, jika tidak ada set .

Søren D. Ptæus
sumber
1
Apakah mereka memiliki makna yang berbeda sangat tergantung pada konteks. Seringkali, untuk tujuan logika apa pun yang ada, mereka mewakili konsep yang setara. Tidak selalu , tetapi sering.
jpmc26
1
@ jpmc26: Saya pikir inti dari jawaban ini adalah untuk mengatakan bahwa dengan pedoman pengkodean untuk c # mereka memiliki arti yang berbeda. Anda selalu dapat membuat kode yang benar di mana nol dan kosong adalah sama jika Anda mau. Dan dalam beberapa kasus Anda juga dapat melakukan hal yang sama apakah itu nol atau kosong tetapi itu tidak berarti bahwa mereka berarti hal yang sama.
Chris
@ Chris Dan saya mengatakan bahwa pedoman pengkodean dari orang-orang yang merancang bahasa tidak dapat digunakan sebagai pengganti untuk mengevaluasi kode Anda dalam konteks persyaratan Anda, perpustakaan yang Anda gunakan, dan pengembang lain tempat Anda bekerja. Gagasan bahwa mereka tidak akan pernah setara dalam konteks adalah idealisme pie-in-the-sky. Mereka bahkan mungkin tidak setara dengan data yang ada pada umumnya tetapi setara untuk menghasilkan hasil dalam beberapa fungsi spesifik yang Anda tulis; dalam hal ini, Anda perlu mendukung keduanya tetapi memastikan mereka menghasilkan hasil yang sama.
jpmc26
Saya mungkin bingung dengan apa yang Anda katakan, tetapi ketika nol dan kosong setara untuk menghasilkan hasil, saya tidak melihat mengapa Anda tidak hanya menggunakan terpisah adalah nol dan kosong daripada ingin metode yang melakukan keduanya sekaligus tetapi tidak memungkinkan Anda untuk membedakan mereka dalam situasi lain ...
Chris
@ Chris Saya maksudkan memiliki konteks yang berbeda (yang sering kali berhubungan dengan cakupan). Pertimbangkan fungsi misalnya. nulldan kosong dapat menghasilkan nilai balik yang sama untuk fungsi, tetapi itu tidak berarti nulldan kosong berarti hal yang persis sama dalam ruang lingkup panggilan.
jpmc26
3

Ada banyak jawaban yang menjelaskan mengapa nulldan kosong berbeda dan cukup banyak pendapat yang mencoba menjelaskan mengapa keduanya harus diperlakukan secara berbeda atau tidak. Namun Anda bertanya:

Apakah saya salah, sebagai klien dari kode ini, untuk berharap bahwa eg ((int []) null). Any () harus mengembalikan false?

Ini harapan yang masuk akal . Anda benar seperti orang lain yang mengadvokasi perilaku saat ini. Saya setuju dengan filosofi implementasi saat ini tetapi faktor pendorong tidak - hanya - didasarkan pada pertimbangan di luar konteks.

Mengingat bahwa Any()tanpa predikat pada dasarnya Count() > 0apa yang Anda harapkan dari cuplikan ini?

List<int> list = null;
if (list.Count > 0) {}

Atau generik:

List<int> list = null;
if (list.Foo()) {}

Saya kira Anda mengharapkan NullReferenceException.

  • Any()adalah metode ekstensi, itu harus lancar diintegrasikan dengan objek yang diperluas kemudian melemparkan pengecualian adalah hal yang paling tidak mengejutkan.
  • Tidak semua bahasa .NET mendukung metode ekstensi.
  • Anda selalu dapat menelepon Enumerable.Any(null)dan di sana Anda pasti berharap ArgumentNullException. Ini adalah metode yang sama dan harus konsisten dengan - mungkin - hampir SEMUANYA dalam Kerangka.
  • Mengakses nullobjek adalah kesalahan pemrograman , kerangka kerja seharusnya tidak menerapkan null sebagai nilai ajaib . Jika Anda menggunakannya seperti itu maka itu adalah tanggung jawab Anda untuk menghadapinya.
  • Ini dogmatis , Anda berpikir salah satu cara dan saya pikir cara lain. Kerangka kerja harus sebanyak mungkin tanpa pilihan.
  • Jika Anda memiliki kasus khusus maka Anda harus konsisten : Anda harus mengambil lebih banyak dan lebih banyak keputusan keras tentang semua metode ekstensi lainnya: jika Count()tampaknya keputusan yang mudah maka Where()tidak. Bagaimana dengan Max()? Itu melempar pengecualian untuk daftar KOSONG, bukankah harus melempar juga untuk nullsatu?

Apa yang dilakukan perancang perpustakaan sebelum LINQ adalah untuk memperkenalkan metode eksplisit ketika nulladalah nilai yang valid (misalnya String.IsNullOrEmpty()) maka mereka HARUS konsisten dengan filosofi desain yang ada . Yang mengatakan, bahkan jika cukup sepele untuk menulis, dua metode EmptyIfNull()dan EmptyOrNull()mungkin berguna.

Adriano Repetti
sumber
1

Jim seharusnya meninggalkan kentang di setiap kantong. Kalau tidak, aku akan membunuhnya.

Jim memiliki tas dengan lima kentang di dalamnya. Apakah ada .Any()kentang di dalam tas?

"Ya," katamu. <= benar

Ok jadi Jim hidup saat ini.

Jim mengeluarkan semua kentang dan memakannya. Apakah ada. Setiap kentang di dalam tas?

"Tidak," katamu. <= salah

Waktunya membunuh Jim.

Jim benar-benar membakar tas itu ke dalam api. Apakah ada. Setiap kentang di dalam tas sekarang?

"Tidak ada tas." <= ArgumentNullException

Haruskah Jim hidup atau mati? Yah kami tidak mengharapkan ini jadi saya perlu putusan. Apakah membiarkan Jim lolos dengan ini bug atau tidak?

Anda dapat menggunakan anotasi untuk memberi sinyal bahwa Anda tidak tahan dengan shenanigans nol apa pun dengan cara ini.

public bool Any( [NotNull] List bag ) 

Tetapi rantai alat Anda harus mendukungnya. Yang berarti Anda kemungkinan masih akan berakhir menulis cek.

candied_orange
sumber
0

Jika ini sangat mengganggu Anda, saya sarankan metode ekstensi sederhana.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Sekarang Anda bisa melakukan ini:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... dan bendera akan disetel ke false.

John Wu
sumber
2
Lebih alami untuk menulis returnpernyataan sebagai:return source ?? Enumerable.Empty<T>();
Jeppe Stig Nielsen
Ini tidak menjawab pertanyaan yang diajukan.
Kenneth K.
@ KennethK.tapi itu tampaknya menyelesaikan masalah yang disajikan.
RQDQ
0

Ini adalah pertanyaan tentang metode ekstensi C # dan filosofi desainnya, jadi saya pikir cara terbaik untuk menjawab pertanyaan ini adalah dengan mengutip dokumentasi MSDN tentang tujuan metode ekstensi :

Metode ekstensi memungkinkan Anda untuk "menambahkan" metode ke tipe yang ada tanpa membuat tipe turunan baru, mengkompilasi ulang, atau memodifikasi tipe asli. Metode ekstensi adalah jenis khusus dari metode statis, tetapi mereka dipanggil seolah-olah mereka adalah contoh metode pada tipe extended. Untuk kode klien yang ditulis dalam C #, F # dan Visual Basic, tidak ada perbedaan yang jelas antara memanggil metode ekstensi dan metode yang sebenarnya didefinisikan dalam suatu tipe.

...

Secara umum, kami menyarankan Anda menerapkan metode ekstensi hemat dan hanya ketika Anda harus. Bilamana mungkin, kode klien yang harus memperluas jenis yang ada harus melakukannya dengan membuat jenis baru yang berasal dari jenis yang ada. Untuk informasi lebih lanjut, lihat Warisan.

Saat menggunakan metode ekstensi untuk memperluas tipe yang kode sumbernya tidak dapat Anda ubah, Anda berisiko bahwa perubahan dalam implementasi tipe akan menyebabkan metode ekstensi Anda rusak.

Jika Anda menerapkan metode ekstensi untuk jenis tertentu, ingat poin-poin berikut:

  • Metode ekstensi tidak akan pernah dipanggil jika memiliki tanda tangan yang sama dengan metode yang didefinisikan dalam tipe.
  • Metode ekstensi dibawa ke ruang lingkup di tingkat namespace. Misalnya, jika Anda memiliki beberapa kelas statis yang berisi metode ekstensi dalam ruang nama tunggal bernama Extensions, mereka semua akan dibawa ke ruang lingkup oleh using Extensions;arahan.

Singkatnya, metode ekstensi dirancang untuk menambahkan metode contoh ke jenis tertentu, bahkan ketika pengembang tidak dapat melakukannya secara langsung. Dan karena metode instance akan selalu menimpa metode ekstensi jika ada (jika dipanggil menggunakan sintaks metode instance), ini hanya boleh dilakukan jika Anda tidak bisa langsung menambahkan metode atau memperluas kelas. *

Dengan kata lain, metode ekstensi harus bertindak seperti metode instance, karena mungkin akhirnya dibuat metode instance oleh beberapa klien. Dan karena metode instance harus melempar jika objek yang dipanggil adalah null, demikian juga metode ekstensi.


* Sebagai catatan tambahan, ini persis situasi yang dihadapi oleh para perancang LINQ: ketika C # 3.0 dirilis, sudah ada jutaan klien yang menggunakan System.Collections.IEnumerabledan System.Collections.Generic.IEnumerable<T>, baik dalam koleksi mereka maupun dalam foreachloop. Kelas-kelas ini kembali IEnumeratorbenda-benda yang hanya memiliki dua metode Currentdan MoveNext, sehingga menambahkan tambahan apapun metode instan diperlukan, seperti Count, Any, dll, akan melanggar jutaan ini klien. Jadi, untuk menyediakan fungsionalitas ini (terutama karena dapat diimplementasikan dalam hal Currentdan MoveNextdengan relatif mudah), mereka merilisnya sebagai metode ekstensi, yang dapat diterapkan ke semua yang ada saat iniIEnumerablecontoh dan juga dapat diimplementasikan oleh kelas dengan cara yang lebih efisien. Seandainya desainer C # memutuskan untuk merilis LINQ pada hari pertama, itu akan disediakan sebagai metode contoh IEnumerable, dan mereka mungkin akan merancang semacam sistem untuk menyediakan implementasi antarmuka standar dari metode tersebut.

TheHansinator
sumber
0

Saya pikir poin penting di sini adalah bahwa dengan mengembalikan false alih-alih melemparkan pengecualian, Anda mengaburkan informasi yang mungkin relevan bagi pembaca / pengubah kode Anda di masa mendatang.

Jika daftar tersebut mungkin nol, saya akan menyarankan memiliki jalur logika terpisah untuk itu, karena jika tidak di masa depan seseorang dapat menambahkan beberapa logika berbasis daftar (seperti add) ke yang lain {} dari if Anda, menghasilkan pengecualian yang tidak diinginkan bahwa mereka tidak punya alasan untuk memprediksi.

Keterbacaan dan pemeliharaan dapat mengalahkan 'Saya harus menulis kondisi ekstra' setiap saat.

Callum Bradbury
sumber
0

Sebagai aturan umum, saya menulis sebagian besar kode saya untuk mengasumsikan bahwa penelepon bertanggung jawab untuk tidak memberikan saya data nol, sebagian besar untuk alasan yang diuraikan dalam Say No to Null (dan posting serupa lainnya). Konsep nol sering tidak dipahami dengan baik, dan untuk alasan itu, disarankan untuk menginisialisasi variabel Anda sebelumnya, sama praktisnya. Dengan asumsi Anda bekerja dengan API yang masuk akal, idealnya Anda tidak akan pernah mendapatkan nilai nol kembali, jadi Anda tidak perlu memeriksa nol. Inti dari nol, seperti yang dinyatakan oleh jawaban lain, adalah untuk memastikan bahwa Anda memiliki objek yang valid (mis. "Tas") untuk digunakan sebelum melanjutkan. Ini bukan fitur Any, tetapi fitur bahasadiri. Anda tidak dapat melakukan sebagian besar operasi pada objek null, karena tidak ada. Ini seperti jika saya meminta Anda untuk pergi ke toko di mobil saya, kecuali saya tidak memiliki mobil, jadi Anda tidak memiliki cara untuk menggunakan mobil saya untuk pergi ke toko. Tidak masuk akal untuk melakukan operasi pada sesuatu yang secara harfiah tidak ada.

Ada beberapa kasus praktis untuk menggunakan null, seperti ketika Anda benar-benar tidak memiliki informasi yang diminta tersedia (misalnya jika saya bertanya kepada Anda berapa umur saya, pilihan yang paling mungkin bagi Anda untuk menjawab pada saat ini adalah "Saya tidak tahu ", yang merupakan nilai nol). Namun, dalam kebanyakan kasus, Anda setidaknya harus tahu apakah variabel Anda diinisialisasi atau tidak. Dan jika tidak, maka saya akan merekomendasikan Anda untuk memperketat kode Anda. Hal pertama yang saya lakukan dalam setiap program atau fungsi adalah menginisialisasi nilai sebelum saya menggunakannya. Jarang saya perlu memeriksa nilai nol, karena saya dapat menjamin bahwa variabel saya tidak nol. Merupakan kebiasaan yang baik untuk masuk, dan semakin sering Anda ingat untuk menginisialisasi variabel Anda, semakin sering Anda perlu memeriksa nol.

phyrfox
sumber