Apakah ada "kebalikan" dari operator penggabungan nol? (… Dalam bahasa apa pun?)

93

penggabungan nol diterjemahkan secara kasar menjadi return x, unless it is null, in which case return y

Saya sering membutuhkan return null if x is null, otherwise return x.y

Saya bisa menggunakan return x == null ? null : x.y;

Lumayan, tapi yang nulldi tengah selalu menggangguku - sepertinya berlebihan. Saya lebih suka sesuatu seperti return x :: x.y;, di mana yang mengikuti ::dievaluasi hanya jika yang mendahuluinya tidak null.

Saya melihat ini sebagai hampir berlawanan ke nol koalesensi, jenis campuran di dengan singkat, inline null-cek, tapi aku [ hampir ] tertentu yang tidak ada operator seperti di C #.

Apakah ada bahasa lain yang memiliki operator seperti itu? Jika ya, apa namanya?

(Saya tahu bahwa saya dapat menulis metode untuk itu di C #; Saya menggunakan return NullOrValue.of(x, () => x.y);, tetapi jika Anda memiliki sesuatu yang lebih baik, saya ingin melihatnya juga.)

Jay
sumber
Beberapa orang meminta sesuatu seperti x? .Y di C #, tapi tidak ada yang seperti itu.
Anthony Pegram
1
@Anthony Oh, itu akan indah. Terima kasih.
Jay
2
Dalam c ++, itu cukup mudah untuk mengekspresikan sebagai return x ? x.y : NULL. Hore untuk mengonversi jenis penunjuk ke boolean!
Phil Miller
1
@Novelocrat yang merupakan salah satu hal yang paling mengganggu saya di C # adalah bahwa mereka tidak mengikuti C jika jika (ada) = benar kecuali jika itu jika (0, salah, nol)
Chris Marisic
2
@ Chris: itu bukan pernyataan yang akurat tentang C. Jika Anda memiliki variabel non-skalar (seperti struct), Anda tidak dapat menggunakannya dalam kondisi.
Phil Miller

Jawaban:

62

Ada operator dereferencing yang tidak aman (?.) Di Groovy ... Saya pikir itulah yang Anda cari.

(Ini juga disebut operator navigasi yang aman .)

Sebagai contoh:

homePostcode = person?.homeAddress?.postcode

Ini akan memberikan null jika person, person.homeAddressatau person.homeAddress.postcodenull.

(Ini sekarang tersedia di C # 6.0 tetapi tidak di versi sebelumnya)

Jon Skeet
sumber
1
Groovy juga memiliki "operator Elvis", yang memungkinkan nilai default selain null, misalnya:def bar = foo ?: "<null>"
Craig Stuntz
28
Saya menominasikan fitur ini untuk C # 5.0. Saya tidak tahu atau peduli apa sebenarnya Groovy itu, tetapi ini cukup intuitif untuk digunakan dalam bahasa apa pun.
György Andrasek
Namun itu sedang ditambahkan ke C # 6 sekarang, hore.
Jeff Mercado
1
Karya Aman-navigasi untuk contoh sepele diposting, tapi Anda masih perlu menggunakan operator ternary jika Anda berniat untuk menggunakan nilai non-null dalam panggilan fungsi, misalnya: Decimal? post = pre == null ? null : SomeMethod( pre );. Alangkah baiknya jika saya bisa menguranginya menjadi "Decimal? Post = pre :: SomeMethod (pre);"
Dai
@Dai: Mengingat jumlah permutasi yang mungkin Anda inginkan, saya cukup senang dengan apa yang kami miliki.
Jon Skeet
36

Kami mempertimbangkan untuk menambahkan?. ke C # 4. Itu tidak berhasil; ini adalah fitur "menyenangkan untuk dimiliki", bukan fitur "harus dimiliki". Kami akan mempertimbangkannya lagi untuk versi hipotetis masa depan dari bahasa tersebut, tetapi saya tidak akan menahan napas menunggu jika saya menjadi Anda. Ini tidak mungkin menjadi lebih penting seiring berjalannya waktu. :-)

Eric Lippert
sumber
Apakah kemudahan menerapkan fitur memengaruhi keputusan untuk menambahkannya? Saya menanyakan hal ini karena menerapkan operator itu tampaknya seperti penambahan bahasa yang cukup lurus ke depan dan sederhana. Saya bukan ahli (hanya lulusan baru dengan cinta yang besar untuk C #), jadi tolong koreksi saya!
Nick Strupat
14
@nick: Tentu, mengimplementasikan lexer dan parser membutuhkan waktu lima menit. Kemudian Anda mulai mengkhawatirkan hal-hal seperti "apakah mesin IntelliSense menanganinya dengan baik?" dan "bagaimana sistem pemulihan kesalahan menghadapinya saat Anda mengetik? tapi belum.?" dan seberapa banyak hal berbeda yang melakukan "." maksudnya di C #, dan berapa banyak dari mereka yang layak untuk memiliki? sebelumnya, dan apa yang seharusnya menjadi pesan kesalahan jika Anda salah, dan berapa banyak pengujian yang dibutuhkan fitur ini (petunjuk: banyak.) Dan bagaimana kita akan mendokumentasikannya dan mengkomunikasikan perubahannya, dan ...
Eric Lippert
3
@ nick: Untuk menjawab pertanyaan Anda - ya, biaya implementasi adalah faktor, tapi kecil. Tidak ada fitur murah di tingkat kami bekerja, hanya fitur yang lebih atau lebih murah. Pekerjaan dev senilai lima dolar untuk membuat parser berfungsi dalam kasus kode yang benar dapat dengan mudah menghasilkan desain, implementasi, pengujian, dokumentasi, dan pendidikan senilai puluhan ribu dolar.
Eric Lippert
11
@EricLippert Saya pikir ini akan menjadi salah satu "menyenangkan untuk dimiliki" UTAMA, yang telah membuat C # begitu sukses dan juga akan melayani misi dengan sempurna membuat kode sesingkat dan ekspresif mungkin!
Konstantin
Saya ingin melihat ini ditambahkan, yang BENAR-BENAR saya inginkan adalah operator elvis: stackoverflow.com/questions/2929836/…
Chris Marisic
16

Jika Anda memiliki jenis khusus logika boolean hubung singkat, Anda dapat melakukan ini (contoh javascript):

return x && x.y;

Jika xnol, maka tidak akan dievaluasi x.y.

Eric
sumber
3
Kecuali ini juga sirkuit pendek pada 0, "" dan NaN, jadi ini bukan kebalikan dari ??. Ini kebalikan dari ||.
ritaj
7

Rasanya tepat untuk menambahkan ini sebagai jawaban.

Saya kira alasan mengapa tidak ada hal seperti itu di C # adalah karena, tidak seperti operator penggabungan (yang hanya berlaku untuk jenis referensi), operasi sebaliknya dapat menghasilkan referensi atau jenis nilai (yaitu class xdengan anggotaint y - oleh karena itu sayangnya akan tidak dapat digunakan dalam banyak situasi.

Saya tidak mengatakan, bagaimanapun, bahwa saya tidak ingin melihatnya!

Solusi potensial untuk masalah itu akan membuat operator secara otomatis mengangkat ekspresi tipe nilai di sisi kanan ke nullable. Tapi kemudian Anda memiliki masalah bahwa di x.ymana y adalah int akan benar-benar kembali dan int?itu akan menyebalkan.

Solusi lain, mungkin lebih baik, bagi operator untuk mengembalikan nilai default (yaitu nol atau nol) untuk tipe di sisi kanan jika ekspresi di sebelah kiri adalah nol. Tapi kemudian Anda memiliki masalah membedakan skenario di mana nol / nol sebenarnya dibaca dari x.yatau apakah itu disediakan oleh operator akses aman.

Andras Zoltan
sumber
ketika saya pertama kali membaca pertanyaan OP, masalah yang sebenarnya muncul di kepala saya, tetapi saya tidak tahu bagaimana mengartikulasikannya. Ini masalah yang sangat serius. +1
rmeador
itu bukan masalah serius. Cukup gunakan int?sebagai nilai pengembalian default, dan pengguna dapat mengubahnya menjadi int jika dia mau. Misalnya jika saya ingin memanggil beberapa metode Lookupdengan nilai default -1 jika salah satu referensi saya adalah null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie
Bagaimana jika ?. :menjadi operator terner, di mana sisi kanan harus memiliki tipe yang sama dengan anggota yang sesuai yang diberikan di tengah?
supercat
6

Delphi memiliki operator: (bukan.), Yang aman-null.

Mereka berpikir untuk menambahkan?. operator ke C # 4.0 untuk melakukan hal yang sama, tetapi itu mendapat blok pemotongan.

Sementara itu, ada IfNotNull () jenis goresan yang gatal. Ini pasti lebih besar dari?. atau:, tetapi memungkinkan Anda membuat rangkaian operasi yang tidak akan mengganggu NullReferenceException pada Anda jika salah satu anggotanya null.

48klocs
sumber
Bisakah Anda memberi contoh penggunaan operator ini?
Max Carroll
1
Ini ?.adalah fitur bahasa C # 6.0, dan ya itu persis seperti yang diminta OP di sini. Lebih baik terlambat daripada tidak sama sekali ^^
T_D
3

Di Haskell, Anda dapat menggunakan >>operator:

  • Nothing >> Nothing adalah Nothing
  • Nothing >> Just 1 adalah Nothing
  • Just 2 >> Nothing adalah Nothing
  • Just 2 >> Just 1 adalah Just 1
dave4420
sumber
3

Haskell punya fmap, yang dalam hal ini menurut saya setara dengan Data.Maybe.map. Haskell murni berfungsi, jadi apa yang Anda cari adalah yang Anda cari

fmap select_y x

Jika xadalah Nothing, hasil ini Nothing. Jika xadalah Just object, hasil ini Just (select_y object). Tidak secantik notasi titik, tetapi mengingat ini adalah bahasa fungsional, gayanya berbeda.

Norman Ramsey
sumber
2

PowerShell memungkinkan Anda mereferensikan properti (tetapi tidak memanggil metode) pada referensi null dan akan mengembalikan null jika instansinya null. Anda dapat melakukan ini di kedalaman apa pun. Saya berharap fitur dinamis C # 4 akan mendukung ini, tetapi ternyata tidak.

$x = $null
$result = $x.y  # $result is null

$x = New-Object PSObject
$x | Add-Member NoteProperty y 'test'
$result = $x.y  # $result is 'test'

Ini tidak bagus tetapi Anda dapat menambahkan metode ekstensi yang akan berfungsi seperti yang Anda gambarkan.

public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector) {
    if (obj == null) { return default(TResult); }
    else { return selector(obj); }
}

var myClass = new MyClass();
var result = myClass.SafeGet(x=>x.SomeProp);
Josh
sumber
2
public class ok<T> {
    T s;
    public static implicit operator ok<T>(T s) { return new ok<T> { s = s }; }
    public static implicit operator T(ok<T> _) { return _.s; }

    public static bool operator true(ok<T> _) { return _.s != null; }
    public static bool operator false(ok<T> _) { return _.s == null; }
    public static ok<T> operator &(ok<T> x, ok<T> y) { return y; }
}

Saya sering membutuhkan logika ini untuk string:

using ok = ok<string>;

...

string bob = null;
string joe = "joe";

string name = (ok)bob && bob.ToUpper();   // name == null, no error thrown
string boss = (ok)joe && joe.ToUpper();   // boss == "JOE"
Harga J Bryan
sumber
1

Buat instance statis kelas Anda di suatu tempat dengan semua nilai default yang tepat untuk anggota.

Sebagai contoh:

z = new Thingy { y=null };

lalu alih-alih file

return x != null ? x.y : null;

kamu bisa menulis

return (x ?? z).y;
Nick
sumber
Saya kadang-kadang menggunakan teknik itu (misalnya (x ?? "") .ToString ()) tetapi hanya praktis pada beberapa tipe data seperti POD dan string.
Qwertie
1

Yang disebut "operator bersyarat nol" telah diperkenalkan di C # 6.0 dan Visual Basic 14.
Dalam banyak situasi ini dapat digunakan sebagai kebalikan dari operator penggabungan-nol:

int? length = customers?.Length; // null if customers is null   
Customer first = customers?[0];  // null if customers is null  
int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

Jpsy
sumber