Boş birleştirme operatörünün "zıttı" var mı? (… Herhangi bir dilde?)


93

boş birleştirme kabaca çevirir return x, unless it is null, in which case return y

Sık sık ihtiyacım var return null if x is null, otherwise return x.y

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

Fena değil, ama nullortadaki her zaman beni rahatsız ediyor - gereksiz görünüyor. Şunun gibi bir şeyi tercih ederim return x :: x.y;, takip eden ::şeyin ancak öncekinin değilse değerlendirildiği null.

Bunu boş birleşmenin neredeyse zıttı olarak görüyorum , kısa, satır içi sıfır kontrolüyle karışık bir şekilde, ama C # 'da böyle bir operatör olmadığından [ neredeyse ] eminim.

Böyle bir operatörü olan başka diller var mı? Varsa ne denir?

(Bunun için C # ile bir yöntem yazabileceğimi biliyorum; kullanıyorum return NullOrValue.of(x, () => x.y);, ancak daha iyi bir şeyiniz varsa, onu da görmek isterim.)


Bazıları C # 'da x? .Y gibi bir şey istedi, ancak bunun gibi bir şey yok.
Anthony Pegram

1
@Anthony Oh, bu çok güzel olurdu. Teşekkürler.
Jay

2
C ++ 'da bu return x ? x.y : NULL,. İşaretçi türlerini boolelere dönüştürmek için yay!
Phil Miller

1
Bu @Novelocrat Beni en C # ki (bir şey) eğer bu kadar dışında = true eğer C takip etmedi ki rahatsız ettiği şeylerden biridir (0, yalancı, null) ise
Chris Marisic

2
@Chris: Bu C hakkında doğru bir ifade değil. Skaler olmayan bir değişkeniniz varsa (bir yapı gibi), bunu bir durumda kullanamazsınız.
Phil Miller

Yanıtlar:


62

Groovy'de boş güvenlikli dereferencing operatörü (?.) Var ... Sanırım peşinde olduğun şey bu.

(Aynı zamanda güvenli navigasyon operatörü olarak da adlandırılır .)

Örneğin:

homePostcode = person?.homeAddress?.postcode

Bu null adlı eğer verecektir person, person.homeAddressya da person.homeAddress.postcodeboş.

(Bu artık C # 6.0'da mevcuttur, ancak önceki sürümlerde yoktur)


1
Groovy'de ayrıca null, örneğin aşağıdakiler dışında varsayılan değerlere izin veren "Elvis operatörü" vardır :def bar = foo ?: "<null>"
Craig Stuntz

28
Bu özelliği C # 5.0 için aday gösteriyorum. Groovy'nin gerçekte ne olduğunu bilmiyorum ya da umrumda değil, ancak bu herhangi bir dilde kullanmak için yeterince sezgisel.
György Andrasek

Ancak şimdi C # 6'ya ekleniyor, yaşasın.
Jeff Mercado

1
Önemsiz örneğin kasa-navigasyon eserler yayınlanmıştır, ama yine de bir işlev çağrısında boş olmayan bir değer kullanmak istiyorsanız, örneğin üçlü operatörü kullanmak gerekir: Decimal? post = pre == null ? null : SomeMethod( pre );. "Decimal? Post = pre :: SomeMethod (pre);"
Dai

@Dai: Bundan isteyebileceğiniz permütasyonların sayısı göz önüne alındığında, sahip olduğumuz şeyden yeterince memnunum.
Jon Skeet

36

Eklemeyi düşündük? C # 4'e. Kesimi yapmadı; bu "sahip olmak güzel" bir özellik, "sahip olunması gereken" bir özellik değil. Bunu dilin gelecekteki varsayımsal versiyonları için tekrar ele alacağız, ama senin yerinde olsam nefesimi beklemem. Zaman geçtikçe daha da önemli hale gelme ihtimali yok. :-)


Bir özelliği uygulama kolaylığı onu ekleme kararında rol oynuyor mu? Bunu soruyorum çünkü bu operatörü uygulamak dile oldukça basit, basit bir ekleme gibi görünüyor. Uzman değilim (sadece C # için büyük sevgiyle yeni bir mezun), lütfen beni düzeltin!
Nick Strupat

14
@nick: Elbette, lexer ve ayrıştırıcıyı uygulamak beş dakikalık bir iştir. Ardından "IntelliSense altyapısı bunu iyi hallediyor mu?" Gibi şeyler hakkında endişelenmeye başlıyorsunuz. ve "hata kurtarma sistemi, siz? yazıp henüz yazmadığınızda bununla nasıl başa çıkıyor?" ve ne kadar farklı şey yapar "." C # anlamına gelir ve bunlardan kaçı a sahip olmayı hak ediyor? ondan önce ve yanlış yaparsanız hata mesajları ne olmalı ve bu özelliğin ne kadar test edilmesi gerekecek (ipucu: çok.) Ve bunu nasıl belgeleyeceğiz ve değişikliği nasıl ileteceğiz ve ...
Eric Lippert

3
@nick: Sorunuza cevap vermek gerekirse - evet, uygulama maliyeti bir faktör, ancak küçük bir faktör. Çalıştığımız seviyede ucuz özellik yok, sadece az ya da çok pahalı özellikler. Ayrıştırıcının doğru kod durumunda çalışmasını sağlamak için yapılan beş dolarlık geliştirme çalışması, kolayca on binlerce dolar değerinde tasarım, uygulama, test, dokümantasyon ve eğitime dönüşebilir.
Eric Lippert

11
@EricLippert Bence bu, C # 'ı bu kadar başarılı kılan ve aynı zamanda kodu olabildiğince kısa ve anlamlı hale getirme misyonuna mükemmel bir şekilde hizmet eden BÜYÜK "sahip olmak güzeller" den biri olurdu!
Konstantin

Bunun eklendiğini görmek istiyorum, GERÇEKTEN istediğim şey elvis operatörü: stackoverflow.com/questions/2929836/…
Chris Marisic

16

Özel bir tür kısa devre mantıksal mantığınız varsa, bunu yapabilirsiniz (javascript örneği):

return x && x.y;

Eğer xnull, o zaman değerlendirmek olmaz x.y.


3
Bunun dışında 0, "" ve NaN üzerindeki kısa devreler, yani ?? nın tersi değil. || 'nin tam tersi.
ritaj

7

Bunu bir cevap olarak eklemek doğru geldi.

Sanırım C # 'da böyle bir şeyin olmamasının nedeni, birleştirme operatöründen farklı olarak (sadece referans türleri için geçerlidir), ters işlemin bir referans veya değer türü (yani class xüye ile int y- dolayısıyla maalesef birçok durumda kullanılamaz.

Ancak bunu görmek istemediğimi söylemiyorum!

Bu soruna olası bir çözüm, operatörün sağ taraftaki bir değer türü ifadesini otomatik olarak boş değer atanabilir hale getirmesini sağlar. Ama sonra, x.yy'nin int olduğu yerde aslında int?bir acı veren bir döndürme sorunu yaşarsınız.

Başka, muhtemelen daha iyi bir çözüm, operatörün, soldaki ifade boş ise sağ taraftaki tür için varsayılan değeri (yani null veya sıfır) döndürmesidir. Ancak, sıfır / boş değerin gerçekten okunduğu x.yveya güvenli erişim operatörü tarafından sağlanıp sağlanmadığı senaryoları ayırt etme sorunları yaşarsınız .


OP'nin sorusunu ilk okuduğumda, bu sorun aklıma geldi, ancak bunu nasıl ifade edeceğimi çözemedim. Bu çok ciddi bir problem. +1
rmeador

ciddi bir sorun değil. int?Varsayılan dönüş değeri olarak kullanın ve kullanıcı isterse bunu int olarak değiştirebilir. Örneğin Lookup, referanslarımdan birinin aşağıdaki gibi olması durumunda varsayılan değeri -1 olan bir yöntemi çağırmak nullfoo?.Bar?.Lookup(baz) ?? -1
istersem

?. :Sağ tarafın ortada verilen uygun üye ile aynı türde olması gereken üçlü bir operatör olmaya ne dersiniz ?
supercat

6

Delphi, null güvenli olan: (. Yerine) operatörüne sahiptir.

Bir? Eklemeyi düşünüyorlardı. aynı şeyi yapmak için operatör C # 4.0'a, ancak bu kesme bloğunu aldı.

Bu arada, kaşıntı yapan IfNotNull () var. Kesinlikle daha büyük? veya:, ancak üyelerden biri boşsa size bir NullReferenceException oluşturmayacak bir işlem zinciri oluşturmanıza izin verir.


Bu operatörün kullanımına bir örnek verebilir misiniz lütfen?
Max Carroll

1
Bu ?.bir C # 6.0 dil özelliğidir ve evet, OP'nin burada istediği şeyi tam olarak yapar. Geç kalmak hiç olmamasından iyidir ^^
T_D

3

Haskell'de >>operatörü kullanabilirsiniz :

  • Nothing >> Nothing dır-dir Nothing
  • Nothing >> Just 1 dır-dir Nothing
  • Just 2 >> Nothing dır-dir Nothing
  • Just 2 >> Just 1 dır-dir Just 1

3

Haskell var fmap, bence bu durumda eşdeğerdir Data.Maybe.map. Haskell tamamen işlevseldir, yani aradığınız şey

fmap select_y x

Eğer xöyleyse Nothing, bu geri döner Nothing. Eğer xöyleyse Just object, bu geri döner Just (select_y object). Nokta notasyonu kadar güzel değil, ancak işlevsel bir dil olduğu düşünüldüğünde, stiller farklıdır.


2

PowerShell, null bir başvuruda özelliklere (ancak yöntemleri çağırmayın) başvurmanıza izin verir ve örnek null ise null döndürür. Bunu herhangi bir derinlikte yapabilirsiniz. C # 4'ün dinamik özelliğinin bunu destekleyeceğini ummuştum ama desteklemiyor.

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

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

Güzel değil ama tarif ettiğiniz şekilde işleyecek bir uzatma yöntemi ekleyebilirsiniz.

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);

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; }
}

Dizeler için genellikle bu mantığa ihtiyacım var:

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"

1

Üyeler için tüm doğru varsayılan değerlere sahip bir yerde sınıfınızın statik bir örneğini oluşturun.

Örneğin:

z = new Thingy { y=null };

o zaman senin yerine

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

Yazabilirsin

return (x ?? z).y;

Bu tekniği bazen kullanıyorum (örneğin (x ?? "") .ToString ()) ama bu sadece POD'lar ve dizeler gibi birkaç veri türünde pratik.
Qwertie


1

Sözde "boş koşullu işleç", C # 6.0 ve Visual Basic 14'te tanıtılmıştır.
Çoğu durumda, boş birleştirme operatörünün tam tersi olarak kullanılabilir:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.