Null Dizeleri Concatenate ama "null.ToString ()" Çağrısı Neden Geçerli?


126

Bu geçerli C # kodudur

var bob = "abc" + null + null + null + "123";  // abc123

Bu geçerli C # kodu değil

var wtf = null.ToString(); // compiler error

İlk ifade neden geçerlidir?


97
Size null.ToString()adın verilmesini garip buluyorum wtf. Bu seni neden şaşırttı? İlk başta çağıracak hiçbir şeyiniz olmadığında bir örnek yöntemi çağıramazsınız.
BoltClock

11
@BoltClock: Elbette boş bir örnekte bir örnek yöntemi çağırmak mümkündür. C # ile mümkün değil, ancak CLR için çok geçerli :)
leppie

1
String.Concat ile ilgili cevaplar neredeyse doğrudur. Aslında, sorudaki spesifik örnek, sabit bölme örneğidir ve ilk satırdaki boş değerler derleyici tarafından elimine edilir - yani artık mevcut olmadıklarından çalışma zamanında asla değerlendirilmezler - derleyici bunları silmiştir. Daha fazla bilgi için burada Dizelerin bitiştirilmesi ve sürekli katlanması için tüm kurallar hakkında küçük bir monolog yazdım: stackoverflow.com/questions/9132338/… .
Chris Shain

77
Bir dizgeye hiçbir şey ekleyemezsiniz ama yoktan bir dizi oluşturamazsınız.
RGB

İkinci ifade geçerli değil mi? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

Yanıtlar:


163

İlk çalışmanın nedeni:

Gönderen MSDN :

Dize birleştirme işlemlerinde, C # derleyicisi boş bir dizeyi boş bir dizeyle aynı şekilde ele alır, ancak orijinal boş dizenin değerini dönüştürmez.

+ İkili operatör hakkında daha fazla bilgi :

İkili + operatörü, işlenenlerden biri veya her ikisi dize türünde olduğunda dize birleştirme gerçekleştirir.

Dize birleştirme işleneni null ise, boş bir dizge değiştirilir. Aksi takdirde, herhangi bir dize olmayan bağımsız değişken, ToStringtür nesnesinden miras alınan sanal yöntemi çağırarak dize temsiline dönüştürülür .

Eğer ToStringdönüşleri null, boş bir dize ikame edilir.

İkinci hatanın sebebi şudur:

null (C # Başvurusu) - null anahtar sözcüğü, herhangi bir nesneye atıfta bulunmayan boş bir başvuruyu temsil eden değişmez bir değerdir. null, başvuru türü değişkenlerin varsayılan değeridir.


1
Bu o zaman işe yarar mı? var wtf = ((String)null).ToString();Son zamanlarda boş dökümün mümkün olduğu Java'da çalışıyorum, C # ile çalışmayalı epey oldu.
Jochem

14
@Jochem: Hala boş bir nesnede bir yöntem çağırmaya çalışıyorsunuz, bu yüzden hayır tahmin ediyorum. null.ToString()Vs olarak düşünün ToString(null).
Svish

@Svish evet, şimdi tekrar düşünüyorum, boş bir nesne, yani haklısın, işe yaramayacak. Java'da da olmaz: boş işaretçi istisnası. Boşver. Cevabınız için Tnx! [düzenleme: Java'da test edildi: NullPointerException. Farkla, cast ile derler, döküm olmadan derlemez].
Jochem

genellikle null anahtar sözcüğünü kasıtlı olarak birleştirmek veya ToString yapmak için bir neden yoktur. Boş bir dizeye ihtiyacınız varsa string.Empty kullanın. bir dize değişkeninin boş olup olmadığını kontrol etmeniz gerekirse, (myString == null) veya string.IsNullOrEmpty (myString) kullanabilirsiniz. Boş bir dize değişkenini dizeye dönüştürmek için alternatif olarak.Empty myNewString = (myString == null ?? string.Empty)
csauve

@Jochem döküm onu ​​derleyecektir ama gerçekten de çalışma zamanında bir NullReferenceException atacak
jeroenh

108

Çünkü +C # 'teki işleç, String.Concatstatik bir yöntem olan dahili olarak çevirir . Ve bu yöntem nullboş bir dizge gibi davranır . String.ConcatReflector'daki kaynağına bakarsanız, görürsünüz:

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN de bundan bahsediyor: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

Öte yandan, ToString()çağıramayacağınız bir örnek yöntemidir null(hangi tür için kullanılmalıdır null?).


2
Yine de String sınıfında + operatörü yoktur .. Yani bu derleyici String.Concat'e çeviri mi yapıyor?
süperolojik

@superlogical Evet, derleyicinin yaptığı budur. Yani aslında, +C # 'da dizelerdeki operatör sadece sözdizimsel şekerdir String.Concat.
Botz3000

Buna ek olarak: Derleyici, en mantıklı olan Concat'ın aşırı yüklenmesini otomatik olarak seçer. Kullanılabilir aşırı yükler 1, 2 veya 3 nesne parametresi, 4 nesne parametresi + __arglistve bir paramsnesne dizisi sürümüdür.
Wormbo

Orada hangi dize dizisi değiştiriliyor? ConcatBir dizi boş olmayan dizge verildiğinde bile her zaman yeni bir boş olmayan dizge dizisi yaratır mı , yoksa başka bir şey mi oluyor?
supercat

@supercat Değiştirilmiş dizi, daha sonra özel bir yardımcı yönteme iletilen yeni bir dizidir. Reflektör kullanarak koda kendiniz bakabilirsiniz.
Botz3000

29

İlk numune çevrilecektir:

var bob = String.Concat("abc123", null, null, null, "abs123");

ConcatYöntem kontrolleri giriş ve boş bir dize olarak null adlı çevirmek

İkinci numune, tercüme edilecek:

var wtf = ((object)null).ToString();

Yani burada bir nullreferans istisnası oluşturulacak


Aslında, a AccessViolationExceptionatılacak :)
leppie

Az önce yaptım :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
NullReferenceException'ım var. DN 4.0
Viacheslav Smityukh

İlginç. Ben koyduğunuzda ((object)null).ToString()bir iç try/catchalıyorum NullReferenceExceptionda.
leppie

3
@Ieppie bunun dışında AccessViolation'ı da atmıyor (neden olsun ki?) - NullReferenceException burada.
jeroenh

11

Kodunuzun ilk bölümü sadece olduğu gibi kabul edilir String.Concat,

bu, dizeleri eklediğinizde C # derleyicisinin çağırdığı şeydir. " abc" + nullİçin tercüme String.Concat("abc", null),

ve içten, bu yöntem yerini nullile String.Empty. Bu yüzden kodun ilk bölümünüz herhangi bir istisna oluşturmaz. aynen

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

Ve kodunuzun 2. bölümünde istisna atar çünkü 'null' bir nesne değildir , null anahtar kelime herhangi bir nesneye atıfta bulunmayan boş bir referansı temsil eden değişmez bir değerdir . null, başvuru türü değişkenlerin varsayılan değeridir.

Ve ' ToString()', bir nesnenin bir örneği tarafından çağrılabilen, ancak herhangi bir değişmez değil.


3
String.Emptyeşit değildir null. Gibi bazı yöntemlerde aynı şekilde ele alınır String.Concat. Örneğin, olarak ayarlanmış bir dize değişkeniniz varsa, üzerinde bir yöntem çağırmaya çalışırsanız nullC # ile değiştirilmez String.Empty.
Botz3000

3
Neredeyse. C # tarafından nullolduğu gibi ele alınmaz String.Empty, sadece içinde bu şekilde ele alınır String.Concat, dizeleri eklediğinizde C # derleyicisinin çağırdığı şeydir. "abc" + nulliçin tercüme String.Concat("abc", null)ve içten, bu yöntem cümledeki nullile String.Empty. İkinci kısım tamamen doğrudur.
Botz3000

9

.Net'ten önce gelen COM çerçevesinde, bir dizge alan herhangi bir yordamın, tamamlandığında onu serbest bırakması gerekiyordu. Boş dizelerin rutinlere girip çıkması çok yaygın olduğundan ve bir boş göstericiyi "serbest bırakmaya" çalışmak meşru bir hiçbir şey yapmama işlemi olarak tanımlandığından, Microsoft bir boş dizge işaretçisinin boş bir dizeyi temsil etmesine karar verdi.

COM ile bir miktar uyumluluğa izin vermek için, .net'teki birçok rutin, boş bir nesneyi boş bir dizge olarak yasal bir temsil olarak yorumlar. Birkaç küçük değişiklikle .net ve dillerinde (en önemlisi örnek üyelerinin "sanal olarak çağırmayın" ifadesini belirtmesine izin verir), Microsoft, nullString türündeki nesnelerin boş dizeler gibi davranmasını sağlayabilirdi . Microsoft bunu yapmış olsaydı, Nullable<T>biraz daha farklı bir şekilde ( Nullable<String>IMHO'nun yine de yapmış olması gereken bir şeye izin vermek için ) çalışması ve / veya NullableStringçoğunlukla birbirinin yerine geçebilecek String, ancak bir tür tanımlaması gerekecekti. nullgeçerli bir boş dize olarak.

Olduğu gibi, a'nın nullmeşru bir boş dizge olarak kabul edileceği ve diğerlerinde olmayacağı bağlamlar vardır . Çok yararlı bir durum değil, programcıların farkında olması gereken bir durum. Genel olarak, formun ifadeler stringValue.someMembereğer başarısız olur stringValueIS null, ancak parametre olarak dizeleri kabul en çerçeve yöntemleri ve operatörler kabul edecek nullboş bir dize olarak.


5

'+'bir infix operatörüdür. Herhangi bir operatör gibi, gerçekten bir yöntem çağırıyor. Infix olmayan bir sürümü hayal edebilirsiniz"wow".Plus(null) == "wow"

Uygulayıcı böyle bir şeye karar verdi ...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

Yani .. örneğiniz

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

aynı olan

var bob = "abc".Plus("123");  // abc123

Hiçbir noktada null bir dizge olmaz. Bundan null.ToString()farklı değil null.VoteMyAnswer(). ;)


Gerçekten, daha çok benziyor var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. Operatör aşırı yüklemeleri özünde statik yöntemlerdir: msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx Değil miydi var bob = null + "abc";veya özellikle string bob = null + null;geçerli olmayacaktı.
Ben Mosher

Haklısın, sadece bu şekilde açıklarsam çok kafa karıştırıcı olacağımı hissettim.
Nigel Thorne

3

Sanırım , herhangi bir nesneye atıfta bulunmayan gerçek bir bilgi . ToString()bir object.


3

Biri bu tartışma dizisinde yoktan bir dizi oluşturamayacağınızı söyledi . (ki bu düşündüğüm gibi güzel bir ifade). Ama evet - aşağıdaki örnekte gösterildiği gibi :-) yapabilirsiniz :

var x = null + (string)null;     
var wtf = x.ToString();

iyi çalışıyor ve hiç bir istisna oluşturmuyor. Tek fark, boş değerlerden birini bir dizeye çevirmeniz gerekmesidir - (string) dönüşümünü kaldırırsanız , örnek yine derlenir, ancak bir çalışma zamanı istisnası atar: "Operatör '+', işlenenler üzerinde belirsizdir. '<boş>' ve '<boş>' "yazın.

NB Yukarıdaki kod örneğinde, beklediğiniz gibi x'in değeri boş değildir, işlenenlerden birini bir dizeye dönüştürdükten sonra aslında boş bir dizedir.


Bir başka ilginç gerçek de, C # / .NET'te farklı veri türlerini dikkate alırsanız işlenme şeklinin nullher zaman aynı olmamasıdır. Örneğin:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

Regard 1 satır kod parçasındaki: Eğer xbir null tamsayı değişkendir (yani int?) değerini içeren 1, o zaman sonuç alıyoruz <null>geri. Değer içeren bir dizeyse (yorumda gösterildiği gibi) "1", o zaman "1"yerine geri dönüyorsunuz <null>.

Not Ayrıca ilginç: var x = 1;İlk satırı kullanıyorsanız, bir çalışma zamanı hatası alıyorsunuz. Neden? Çünkü atama, değişkeni null yapılamayan xveri türüne dönüştürecektir int. Derleyici int?burada varsaymaz ve bu nedenle nulleklendiği 2. satırda başarısız olur .


2

nullBir dizeye ekleme basitçe göz ardı edilir. null(ikinci örneğinizde) herhangi bir nesnenin bir örneği değildir, bu yüzden bir ToString()yöntemi bile yoktur . Bu sadece gerçek.


1

Orada arasında hiçbir fark olduğu için string.Emptyve nullsiz dizeleri Concat zaman. Siz de boş verebilirsiniz string.Format. Ancak null, her zaman a ile sonuçlanacak NullReferenceExceptionve dolayısıyla bir derleyici hatası oluşturan bir yöntemi çağırmaya çalışıyorsunuz .
Herhangi bir nedenle gerçekten yapmak istiyorsanız, kontrol eden nullve sonra geri dönen bir genişletme yöntemi yazabilirsiniz string.Empty. Ancak bunun gibi bir uzantı yalnızca kesinlikle gerekli olduğunda kullanılmalıdır (bence).


0

Genel olarak: Spesifikasyona bağlı olarak null değerinin parametre olarak kabul edilmesi geçerli olabilir veya olmayabilir, ancak null üzerinde bir yöntemi çağırmak her zaman geçersizdir.


Bu ve diğer bir konu, dizeler durumunda + operatörünün işlenenlerinin null olabileceğidir. Bu, programcıların hayatını kolaylaştırmak için bir çeşit VB şeydir (üzgünüm çocuklar) veya programcının boşlarla baş edemeyeceğini varsayarsak. Bu şartnameye tamamen katılmıyorum. 'bilinmeyen' + 'herhangi bir şey' hala 'bilinmeyen' olmalıdır ...

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.