VB.NET ve C # 'de bir değere karşı null kontrolünde neden bir fark var?


110

In VB.NET Bu durumda:

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false") '' <-- I got this. Why?
End If

Ancak C # 'da şu olur:

decimal? x = default(decimal?);
decimal? y = default(decimal?);

y = 5;
if (x != y)
{
    Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :)
}
else
{
    Debug.WriteLine("false");
}

Neden burada bir fark var?


22
bu korkunç.
Mikeb

8
Sanırım default(decimal?)0 dönüyor, değil null.
Ryan Frame

7
@RyanFrame YOK. O olduğu için null türleri , döndürürnull
Soner Gönül

4
Oh evet ... doğru ... VB Ifkoşullarında mantıksal değer olarak değerlendirmeye gerek yoktur ... uuuugh EDIT: Yani Nothing <> Anything = Nothingbu If, negatif / else yolunu almakla sonuçlanır .
Chris Sinclair

13
@JMK: Null, Nothing ve Empty aslında çok farklı. Hepsi aynı olsaydı, üçüne ihtiyacınız olmazdı.
Eric Lippert

Yanıtlar:


88

VB.NET ve C # .NET, kullanım hakkında farklı varsayımlarda bulunan farklı ekipler tarafından oluşturulan farklı dillerdir; bu durumda bir NULL karşılaştırmasının semantiği.

Benim kişisel tercihim, özünde NULL'a "Henüz bilmiyorum" anlamını veren VB.NET semantiğidir. Sonra 5'in "Henüz bilmiyorum" ile karşılaştırılması. doğal olarak "henüz bilmiyorum"; yani NULL. Bu, SQL veritabanlarında (tümü olmasa da çoğu) NULL davranışını yansıtma ek avantajına sahiptir. Bu aynı zamanda, burada açıklandığı gibi, üç değerli mantığın daha standart (C # 'dan) bir yorumudur .

C # ekibi, NULL'un ne anlama geldiğine dair farklı varsayımlar yaptı ve bu da gösterdiğiniz davranış farkıyla sonuçlandı. Eric Lippert, C # 'ta NULL kelimesinin anlamı hakkında bir blog yazdı . Eric Lippert'e göre: "Ayrıca burada ve burada VB / VBScript ve JScript'teki boş değerlerin anlambilimini yazdım ".

NULL değerlerin mümkün olduğu herhangi bir ortamda, Dışarıda Bırakılan Orta Yasasına (yani A veya A'nın totolojik olarak doğru olduğu) artık güvenilemeyeceğini kabul etmek önemsizdir.

Güncelleme:

A bool(a'nın aksine bool?) yalnızca DOĞRU ve YANLIŞ değerlerini alabilir. Bununla birlikte, NULL'un bir dil gerçeklemesi, NULL'un ifadeler aracılığıyla nasıl yayılacağına karar vermelidir. VB'de ifadeler 5=nullve 5<>nullBOTH yanlış döndürür. Cı benzer ifadeler arasında, içinde 5==nullve 5!=nullsadece , ikinci birinci - [PG 2014/03/02 güncelleme] döner yanlış. Bununla birlikte, null'u destekleyen HERHANGİ bir ortamda, programcının o dil tarafından kullanılan doğruluk tablolarını ve boş yayılımı bilmek görevidir.

Güncelleme

Eric Lippert'in anlambilimle ilgili blog makaleleri (aşağıdaki yorumlarında bahsedilmiştir) şimdi şu adreste:


4
Bağlantı için teşekkürler. Ayrıca burada VB / VBScript ve JScript'teki boş değerlerin semantiğini yazdım: blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx ve burada: blogs.msdn.com/b/ericlippert/ arşiv / 2003/10/01 / 53128.aspx
Eric Lippert

27
Bilginize, C # 'ı bu şekilde VB ile uyumsuz hale getirme kararı tartışmalıydı. O zamanlar dil tasarım ekibinde değildim ama bu karara giden tartışma miktarı oldukça fazlaydı.
Eric Lippert

2
@ BlueRaja-DannyPflughoeft C # ' boolda 3 değer olamaz, sadece iki. Bu var bool?üç değerlere sahip olabilir. operator ==ve operator !=de dönüp bool, değil bool?, ne olursa olsun işlenen türünden. Ek olarak, bir ififade yalnızca a kabul edebilir bool, a değil bool?.
2013

1
C # 'da ifadeler 5=nullve 5<>nullgeçerli değildir. Ve 5 == nullve 5 != null, bunu döner ikinci olduğundan eminseniz false?
Ben Voigt

1
@BenVoigt: Teşekkürler. Tüm bu olumlu oylar ve bu yazım hatasını ilk fark eden sizsiniz. ;-)
Pieter Geerkens

37

Çünkü yerine x <> ydöner . Tanımlanmadığı için basitçe tanımlanmamıştır. (boş SQL'e benzer).Nothingtruex

Not: VB.NET Nothing<> C # null.

Ayrıca a'nın değerini Nullable(Of Decimal)yalnızca bir değeri varsa karşılaştırmanız gerekir.

Dolayısıyla yukarıdaki VB.NET, buna benzer (daha az yanlış görünür):

If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")  
End If

VB.NET dil spesifikasyonu :

7.1.1 Null yapılabilir Değer Türleri ... Null yapılabilir bir değer türü, türün null yapılamayan sürümünün yanı sıra boş değerle aynı değerleri içerebilir. Böylece, boş değer atanabilir bir değer türü için, türdeki bir değişkene Nothing atamak, değişkenin değerini değer türünün sıfır değerine değil, null değerine ayarlar.

Örneğin:

Dim x As Integer = Nothing
Dim y As Integer? = Nothing

Console.WriteLine(x) ' Prints zero '
Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) '

16
"VB.NET Hiçbir şey <> C # null" C # için doğru ve VB.Net için yanlış mı döndürüyor? Şaka yapıyorum :-p
ken2k

17

Oluşturulan CIL'e bakın (Her ikisini de C #'a çevirdim):

C #:

private static void Main(string[] args)
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    decimal? CS$0$0000 = x;
    decimal? CS$0$0001 = y;
    if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
        (CS$0$0000.HasValue != CS$0$0001.HasValue))
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Visual Basic:

[STAThread]
public static void Main()
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
    bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
    if (VB$LW$t_struct$S1.GetValueOrDefault())
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Visual Basic'teki karşılaştırmanın Nullable <bool> (bool, false veya true değil!) Döndürdüğünü göreceksiniz. Ve tanımsız, bool'a dönüştürülmüş yanlıştır.

Nothingher zaman Nothingolanla karşılaştırıldığında , Visual Basic'te yanlış değil (SQL'deki ile aynıdır).


Neden soruya deneme yanılma yoluyla cevap vermelisiniz? Dil spesifikasyonlarından yapmak mümkün olmalıdır.
David Heffernan

3
@DavidHeffernan, çünkü bu, dilin oldukça açık olan farkını gösteriyor.
nothrow

2
@Yossarian Dil özelliklerinin konuyla ilgili belirsiz olduğunu düşünüyorsunuz. Katılmıyorum. IL, değişikliğe tabi bir uygulama ayrıntısıdır; özellikler değil.
2013

2
@DavidHeffernan: Tavrınızı beğendim ve denemeniz için sizi cesaretlendiriyorum. VB dil belirtiminin zaman zaman ayrıştırılması zor olabilir. Lucian bunu birkaç yıldır geliştiriyor, ancak bu tür köşe vakalarının tam anlamlarını bulmak hala oldukça zor olabilir. Spesifikasyonun bir kopyasını almanızı, biraz araştırma yapmanızı ve bulgularınızı rapor etmenizi öneririm.
Eric Lippert

2
@Yossarian Sağladığınız IL kodunu çalıştırmanın sonuçları değişikliğe tabi değildir, ancak sağlanan C # / VB kodunun gösterdiğiniz IL koduna derleneceği değişiklik yapılabilir ( söz konusu IL'nin davranışı olduğu sürece) ayrıca dil özelliklerinin tanımına uygun).
2013

6

Burada gözlemlenen sorun, daha genel bir sorunun özel bir durumudur; en azından bazı durumlarda faydalı olabilecek farklı eşitlik tanımlarının sayısı, bunları ifade etmek için yaygın olarak bulunan araçların sayısını aşmaktadır. Bu sorun, bazı durumlarda, eşitliği test etmenin farklı araçlarının farklı sonuçlar vermesinin kafa karıştırıcı olduğu yönündeki talihsiz bir inançla daha da kötüleştirilir ve bu tür bir kafa karışıklığı, mümkün olduğunda farklı eşitlik biçimlerinin aynı sonuçları vermesini sağlayarak önlenebilir.

Gerçekte, kafa karışıklığının temel nedeni, farklı anlambilimlerin farklı koşullarda yararlı olmasına rağmen, farklı eşitlik ve eşitsizlik testlerinin aynı sonucu vermesinin beklenmesi gerektiğine dair yanlış bir inançtır. Örneğin, aritmetik bir bakış açısından, Decimalyalnızca sondaki sıfırların eşit olarak karşılaştırılmasında farklılık gösterenlere sahip olmak yararlıdır . Aynı şekilde doublepozitif sıfır ve negatif sıfır gibi değerler için . Öte yandan, önbelleğe alma veya interning açısından bu tür anlambilim ölümcül olabilir. Örneğin birinin eşit Dictionary<Decimal, String>olması myDict[someDecimal]gereken bir tür olduğunu varsayalım someDecimal.ToString(). Böyle bir nesne, çok sayıda olsaydı mantıklı görünürdü.Decimaldizeye dönüştürmek istediği ve birçok yinelenen olması beklenen değerler. Ne yazık ki, 12.3 m ve 12.40 m'yi ve ardından 12.30 m ve 12.4 m'yi dönüştürmek için böyle bir önbelleğe alma kullanılırsa, son değerler "12.30" ve "12.4" yerine "12.3" ve "12.40" verir.

Eldeki konuya dönersek, eşitlik açısından sıfırlanabilir nesneleri karşılaştırmanın birden fazla mantıklı yolu vardır. C #, ==operatörünün davranışını yansıtması gereken bakış açısını alır Equals. VB.NET , davranışı isteyen herkesin Equalskullanabileceği için , davranışının diğer dillerinkini yansıtması gerektiği görüşünü alır Equals. Bir anlamda, doğru çözüm, üç yollu bir "if" yapısına sahip olmak ve koşullu ifade üç değerli bir sonuç döndürürse, kodun nulldurumda ne olması gerektiğini belirtmesini gerektirmektir . Bu, diller için bir seçenek olmadığından, bir sonraki en iyi alternatif, farklı dillerin nasıl çalıştığını öğrenmek ve aynı olmadıklarını kabul etmektir.

Bu arada, Visual Basic'in C'de bulunmayan "Is" operatörü, boş değer atanabilir bir nesnenin aslında boş olup olmadığını test etmek için kullanılabilir. Bir iftestin kabul etmesi gerekip gerekmediğini makul bir şekilde sorgulayabilirken , null atanabilir türlerde çağrıldığında yerine Boolean?normal karşılaştırma işleçlerinin dönmesi yararlı bir özelliktir. Bu arada, VB.NET'te, bir kişi yerine eşitlik operatörünü kullanmaya çalışırsa , karşılaştırmanın sonucunun her zaman olacağı ve bir şeyin boş olup olmadığını test etmek isterse kullanılması gerektiği konusunda bir uyarı alacaktır .Boolean?BooleanIsNothingIs


C # 'da bir sınıfın boş olup olmadığının test edilmesi tarafından yapılır == null. Ve null yapılabilir bir değer türünün bir değere sahip olup olmadığını test etmek için .hasValue. Bir Is Nothingoperatörün ne faydası var ? C # vardır isancak tür uyumluluğunu test eder. Bunların ışığında, son paragrafınızın ne demeye çalıştığından gerçekten emin değilim.
ErikE

@ErikE: Hem vb.net hem de C #, null yapılabilir türlerin bir karşılaştırma kullanılarak bir değer için kontrol edilmesine izin verir null, ancak her iki dil de bunu bir HasValuekontrol için sözdizimsel şeker olarak değerlendirse de , en azından türün bilindiği durumlarda (emin değilim jenerikler için hangi kod üretilir).
supercat

Jeneriklerde, null yapılabilir tipler ve aşırı yük çözümü ile ilgili zor problemler yaşayabilirsiniz ...
ErikE

3

Olabilir bu yazılan kuyu size yardım:

Doğru hatırlıyorsam, VB'deki 'Hiçbir Şey', "varsayılan değer" anlamına gelir. Bir değer türü için bu, bir başvuru türü için boş olan varsayılan değerdir. Bu nedenle, bir yapıya hiçbir şey atamamak hiç sorun değil.


3
Bu soruya cevap vermiyor.
David Heffernan

Hayır, hiçbir şeyi açıklığa kavuşturmaz. Soru tamamen <>VB'deki operatörle ve null yapılabilir türlerde nasıl çalıştığıyla ilgilidir.
David Heffernan

2

Bu, VB'nin kesin bir tuhaflığı.

VB'de, iki boş değer atanabilir türü karşılaştırmak istiyorsanız, kullanmalısınız Nullable.Equals().

Örneğinizde şöyle olmalıdır:

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If Not Nullable.Equals(x, y) Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")
End If

5
Tanıdık olmadığında "tuhaflık" oluyor. Pieter Geerkens tarafından verilen cevaba bakınız.
rskar

VB'nin davranışını yeniden üretmemesinin de garip olduğunu düşünüyorum Nullable<>.Equals(). Aynı şekilde çalışması beklenebilir (C #'ın yaptığı gibi).
Matthew Watson

Beklentiler, "beklenebilecekler" gibi, kişinin deneyimledikleriyle ilgilidir. C #, Java kullanıcılarının beklentileri göz önünde bulundurularak tasarlanmıştır. Java, C / C ++ kullanıcılarının beklentileri göz önünde bulundurularak tasarlanmıştır. Daha iyisi veya daha kötüsü için, VB.NET, VB6 kullanıcılarının beklentileri göz önünde bulundurularak tasarlandı. Stackoverflow.com/questions/14837209/… ve stackoverflow.com/questions/10176737/…
adreslerinde

1
@MatthewWatson tanımı Nullable.NET'in ilk sürümlerinde yoktu, C # ve VB.NET bir süre devre dışı kaldıktan ve boş yayılma davranışlarını belirledikten sonra oluşturuldu. Dürüst olmak gerekirse, dilin birkaç yıldır yaratılmayan bir türle tutarlı olmasını mı bekliyorsunuz? Bir VB.NET programcısının bakış açısından, Nullable.Equals, tam tersi değil, dil ile tutarlı değildir. (C # ve VB'nin her ikisinin de aynı Nullabletanımı kullandığı göz önüne alındığında, her iki dille tutarlı olmasının bir yolu yoktu.)
Servy

0

VB kodunuz basitçe yanlıştır - "x <> y" yi "x = y" olarak değiştirirseniz, sonuç olarak yine "yanlış" olacaktır. Null yapılabilir örnekler için bunu en yaygın ifade şekli "Not x.Equals (y)" dir ve bu, C # 'da "x! = Y" ile aynı davranışı verecektir.


1
Aksi takdirde x, nothingbu durumda x.Equals(y)bir istisna atar.
2013

@Servy: (yıllar sonra) yine bu tökezledi ve seni düzeltmek olmadığını fark - "x.Equals (y)" olacak değil 'x' null tip örneği için bir özel durum. Null yapılabilir türler, derleyici tarafından farklı şekilde ele alınır.
Dave Doknjas

Spesifik olarak, 'null' olarak başlatılan null yapılabilir bir örnek gerçekten null olarak ayarlanmış bir değişken değil, değer ayarlanmamış bir System.Nullable örneğidir.
Dave Doknjas
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.