Operator penggabungan properti untuk C #


9

Operator null-penggabungan dalam c # memungkinkan Anda untuk mempersingkat kode

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

Ke:

  return _mywidget ?? new Widget();

Saya terus menemukan bahwa operator yang berguna yang ingin saya miliki di C # akan menjadi salah satu yang memungkinkan Anda untuk mengembalikan properti suatu objek, atau nilai lain jika objek tersebut nol. Jadi saya ingin mengganti

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

Dengan:

  return _mywidget.Length ??! 5;

Saya tidak dapat berhenti berpikir bahwa pasti ada alasan bagi operator ini untuk tidak ada. Apakah ini bau kode? Apakah ada cara yang lebih baik untuk menulis ini? (Saya mengetahui pola objek nol tetapi tampaknya berlebihan untuk menggunakannya untuk mengganti keempat baris kode ini.)


1
Apakah operator bersyarat cukup di sini?
Anon.

1
Seseorang telah menulis sesuatu yang memungkinkan Anda melakukan sesuatu seperti ini: string location = employee.office.address.location ?? "Tidak Dikenal"; . Ini akan mengatur lokasi ke "Tidak Dikenal" ketika salah satu objek (baik karyawan , kantor , alamat atau lokasi ) adalah nol. Sayangnya, saya tidak ingat siapa yang menulisnya atau di mana dia mempostingnya. Jika saya menemukannya lagi, saya akan mempostingnya di sini!
Kristof Claes

1
Pertanyaan ini akan mendapatkan lebih banyak daya tarik pada StackOverflow.
Pekerjaan

2
??!adalah operator di C ++. :-)
James McNellis

3
Halo 2011, baru saja menelepon untuk mengatakan bahwa c # mendapatkan operator navigasi yang aman
Nathan Cooper

Jawaban:


5

Saya sangat menginginkan fitur bahasa C # yang memungkinkan saya melakukan x.Prop.SomeOtherProp.ThirdProp dengan aman tanpa harus memeriksa setiap null. Dalam satu basis kode di mana saya harus sering melakukan "deep property traversals", saya menulis metode ekstensi bernama Navigate yang menelan NullReferenceExceptions dan sebagai gantinya mengembalikan nilai default. Jadi saya bisa melakukan sesuatu seperti ini:

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

Kelemahan dari ini adalah bahwa ia memiliki bau kode yang berbeda: menelan pengecualian. Jika Anda ingin melakukan sesuatu seperti "benar" ini, Anda bisa menggunakan lamdba sebagai ekspresi, dan memodifikasi ekspresi dengan cepat untuk menambahkan cek nol di sekitar setiap accessor properti. Saya memilih "cepat dan kotor" dan tidak menerapkannya dengan cara "lebih baik" ini. Tetapi mungkin ketika saya memiliki beberapa siklus pemikiran cadangan saya akan mengunjungi kembali ini dan mempostingnya di suatu tempat.

Untuk menjawab pertanyaan Anda secara lebih langsung, saya menduga alasan ini bukan fitur adalah karena biaya penerapan fitur melebihi manfaat dari memiliki fitur tersebut. Tim C # harus memilih dan memilih fitur mana yang menjadi fokus, dan yang ini belum melayang ke atas. Kurasa aku tidak punya info orang dalam.


1
Persis seperti yang saya pikirkan, tetapi kinerja cara aman akan sangat buruk kurasa (karena banyak Ekspresi. Kompilasi ()) ... Sayang sekali itu tidak diterapkan di C # (sesuatu seperti Obj.?PropA.?Prop1)
Guillaume86

x.PropOne.PropTwo.PropThree.PropFour adalah pilihan desain yang buruk karena melanggar Hukum Demeter. Desain ulang metode / kelas Anda sehingga Anda tidak perlu menggunakan ini.
Justin Shield

Melarikan diri dari akses properti dalam-dalam tanpa mengingat Hukum Demeter sama berbahayanya dengan normalisasi database tanpa berpikir. Anda bisa melangkah terlalu jauh.
Mir

3
Dalam C # 6.0 ini dimungkinkan dengan menggunakan Null-Conditional Operator (alias operator Elvis) msdn.microsoft.com/en-us/magazine/dn802602.aspx
victorvartan

15

Saya yakin Anda bisa melakukan ini dengan C # 6 cukup sederhana sekarang:

return _mywidget?.Length ?? 5;

Perhatikan operator kondisi nol ?.di sebelah kiri. _mywidget?.Lengthmengembalikan nol jika _mywidgetnol.

Operator ternary sederhana mungkin lebih mudah dibaca, seperti yang disarankan orang lain.


9

Ini benar-benar hanya spekulasi mengapa mereka tidak melakukannya. Lagipula, saya tidak percaya ?? berada di versi C # pertama.

Bagaimanapun, saya hanya akan menggunakan operator kondisional dan menyebutnya sehari:

return (_mywidget != null) ? _mywidget.Length : 5;

Itu ?? hanyalah jalan pintas ke operator bersyarat.


5
Secara pribadi, ini sama buruknya (imho) dengan memiliki operator untuk melakukan ini sejak awal. Anda menginginkan sesuatu dari suatu objek ... objek itu mungkin tidak ada di sana ... jadi mari kita berikan 5jawabannya jika tidak ada. Anda harus melihat desain Anda sebelum mulai meminta operator khusus.
Moo-Juice

1
@ Moo-Juice Saya hanya membayangkan orang yang menjaga kode ini, 'Mengapa ini memberi saya 5? Hmmmm Apa?!'
msarchet

4

Itu terlihat seperti operator ternary:

return (_mywidget != NULL) ? _mywidget : new Widget();

3

Secara pribadi, saya pikir ini mudah dibaca. Pada contoh pertama, ??terjemahannya cukup baik sebagai "Apakah Anda di sana ?? Tidak, mari kita lakukan ini".

Dalam contoh kedua, ??!jelas WTF dan tidak ada artinya bagi programmer .... objek tidak ada, jadi saya tidak bisa mendapatkan di properti jadi saya akan kembali 5. Apa artinya itu? Apa itu 5? Bagaimana kita sampai pada kesimpulan bahwa 5 adalah nilai yang baik?

Singkatnya, contoh pertama masuk akal ... tidak ada di sana, baru itu. Yang kedua, terbuka untuk diperdebatkan.


2
Nah, Anda harus mengabaikan fakta bahwa ??! tidak ada Bayangkan kalau ?? itu biasa, dan jika itu digunakan, maka buatlah keputusan berdasarkan itu.
whatsisname

3
Itu mengingatkan saya pada apa yang disebut "operator WTF" di C ++ (ini benar-benar berfungsi:. (foo() != ERROR)??!??! cerr << "Error occurred" << endl;)
Tamás Szelei

@whatsisname, itu lebih merupakan cerminan dari fakta bahwa itu sesuai untuk keputusan yang dibuat. Untuk membuat objek karena itu tidak ada, masuk akal. Untuk menetapkan nilai arbituary yang tidak berarti apa-apa bagi pembaca karena Anda tidak bisa mendapatkan properti , itu memang skenario WTF.
Moo-Juice

Saya pikir Anda terjebak dalam contoh saya. Mungkin 0 lebih baik dari 5. Mungkin properti adalah objek jadi jika _mywidget adalah null, Anda ingin mengembalikan foodle baru (). Saya benar-benar tidak setuju itu ?? lebih mudah dibaca daripada ??! meskipun :)
Ben Fulton

@ Ben, Meskipun saya setuju bahwa fokus pada operator itu sendiri tidak terlalu banyak memberikan pertanyaan Anda, saya juga menggambar paralel dengan persepsi programmer dan itu sewenang-wenang 5. Saya benar-benar berpikir ada lebih banyak masalah yang harus Anda lakukan seperti ini, sedangkan dalam contoh pertama Anda, kebutuhannya cukup jelas, terutama dalam hal-hal seperti manajer cache.
Moo-Juice

2

Saya pikir orang terlalu terjebak dalam "5" yang Anda kembalikan ... mungkin 0 akan lebih baik :)

Lagi pula, saya pikir masalahnya adalah bahwa ??!sebenarnya bukan operator mandiri. Pertimbangkan apa artinya ini:

var s = myString ??! "";

Dalam hal itu, tidak masuk akal: hanya masuk akal jika operan kiri adalah pengakses properti. Atau bagaimana dengan ini:

var s = Foo(myWidget.Length) ??! 0;

Ada accessor properti di sana, tapi aku masih tidak berpikir itu masuk akal (jika myWidgetadalah null, apakah itu berarti kita tidak menyebut Foo()sama sekali?), Atau ini hanya kesalahan?

Saya pikir masalahnya adalah itu tidak cocok secara alami dalam bahasa seperti ??halnya.


1

Seseorang telah menciptakan kelas utilitas yang akan melakukan ini untuk Anda. Tetapi saya tidak dapat menemukannya. Apa yang saya temukan adalah sesuatu yang serupa di Forum MSDN (lihat jawaban kedua).

Dengan sedikit kerja, Anda dapat memperluasnya untuk mengevaluasi panggilan metode dan ekspresi lain yang tidak didukung sampel. Anda juga dapat memperluasnya untuk menerima nilai default.


1

Saya mengerti dari mana Anda berasal. Sepertinya semua orang dibungkus kapak dengan contoh yang Anda berikan. Sementara saya setuju bahwa angka ajaib adalah ide yang buruk, akan ada gunanya untuk setara dengan operator koallescing nol untuk properti tertentu.

Misalnya, Anda memiliki objek yang terikat pada peta, dan Anda ingin objek berada pada ketinggian yang sesuai. Peta DTED dapat memiliki lubang di data mereka sehingga dimungkinkan untuk memiliki nilai nol, serta nilai dalam kisaran sekitar -100 hingga ~ 8900 meter. Anda mungkin menginginkan sesuatu seperti:

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

Operator pengelompokan nol dalam kasus ini akan mengisi ketinggian standar jika Koordinat belum ditetapkan, atau objek Koordinat tidak dapat memuat data DTED untuk lokasi itu.

Saya melihat ini sebagai sangat berharga, tetapi spekulasi saya mengapa itu tidak dilakukan terbatas pada kompiler kompleksitas. Mungkin ada beberapa kasus di mana perilaku tidak akan dapat diprediksi.


1

Ketika tidak berurusan dengan sangat bersarang saya telah menggunakan ini (sebelum operator penggabungan nol diperkenalkan di C # 6). Bagi saya itu cukup jelas, tapi mungkin itu karena saya sudah terbiasa, orang lain mungkin merasa membingungkan.

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

Tentu saja, ini tidak selalu berlaku, karena kadang-kadang properti yang Anda butuhkan tidak dapat diatur secara langsung, atau ketika konstruksi objek itu sendiri mahal, di antara alasan lainnya.

Alternatif lain adalah dengan menggunakan pola NullObject . Anda mendefinisikan satu instance statis kelas dengan nilai default yang masuk akal, dan menggunakannya sebagai gantinya.

return ( _mywidget ?? myWidget.NullInstance).Length;

0

Angka ajaib biasanya bau. Jika angka 5 adalah angka arbitrer, maka lebih baik untuk mengejanya sehingga didokumentasikan dengan lebih jelas. Pengecualian dalam pendapat saya adalah jika Anda memiliki koleksi nilai yang bertindak sebagai nilai default dalam konteks tertentu.

Jika Anda memiliki satu set nilai default untuk objek, Anda bisa mensubklasifikasikannya untuk membuat instance singleton default.

Sesuatu seperti: return (myobj ?? default_obj). Panjang


0

Properti length biasanya adalah tipe nilai sehingga tidak bisa null, jadi operator null-coalescing tidak masuk akal di sini.

Tetapi Anda dapat melakukannya dengan properti yang merupakan tipe referensi, misalnya: var window = App.Current.MainWindow ?? new Window();


Contohnya adalah mencoba mempertimbangkan apa yang akan terjadi dalam situasi Anda jika saat ini nol. Contoh Anda hanya akan macet ... tetapi memiliki operator yang dapat null menyatu di mana saja dalam rantai penyesatan akan berguna.
Mir

-1

Saya telah melihat seseorang dengan ide serupa sebelumnya tetapi sebagian besar waktu Anda ingin juga menetapkan nilai baru ke bidang nol, sesuatu seperti:

return _obj ?? (_obj = new Class());

jadi idenya adalah untuk menggabungkan ??dan =tugas menjadi:

return _obj ??= new Class();

Saya pikir ini lebih masuk akal daripada menggunakan tanda seru.

Ide ini bukan milikku tapi aku sangat menyukainya :)


1
Apakah Anda seorang korban dari contoh yang buruk? Contoh Anda dapat disingkat menjadi return _obj ?? new Class();tidak perlu untuk di ??=sini.
Matt Ellen

@ Matt Ellen Anda salah. Tugas ini dimaksudkan . Saya menyarankan alternatif lain. Ini tidak persis seperti apa yang diminta OP, tapi saya percaya itu akan sama bermanfaatnya.
chakrit

2
mengapa Anda ingin tugas jika Anda akan mengembalikan nilai?
Matt Ellen

malas inisialisasi?
chakrit
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.