Null + true bir dize nasıl olur?


112

Yana truebir dize türüdür nasıl null + truebir dize?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

Bunun arkasındaki sebep nedir?


27
Mantıklı olmayan ve sonra derleyicinin oluşturduğu mesajı beğenmeyen bir şey mi yapıyorsunuz? Bu geçersiz C # kodu ... tam olarak ne yapmasını istediniz?
Hogan

19
@Hogan: Kod rastgele ve sorunun saf meraktan sorulmasına yetecek kadar akademik görünüyor. Tahminimce ...
BoltClock

8
null + true, JavaScript'te 1 döndürür.
Fatih Acet

Yanıtlar:


147

Göründüğü kadar tuhaf, sadece C # dil spesifikasyonundaki kuralları takip ediyor.

Bölüm 7.3.4'ten:

Op, aşırı yüklenebilir ikili operatör, x, X tipi bir ifade ve y, Y tipi bir ifadedir, aşağıdaki şekilde işlenir:

  • Operatör op (x, y) için X ve Y tarafından sağlanan aday kullanıcı tanımlı operatörler kümesi belirlenir. Set, her biri §7.3.5 kuralları kullanılarak belirlenen, X tarafından sağlanan aday operatörler ve Y tarafından sağlanan aday operatörlerin birleşiminden oluşur. X ve Y aynı türdeyse veya X ve Y ortak bir temel türden türetilmişse, paylaşılan aday operatörler birleşik kümede yalnızca bir kez oluşur.
  • Aday kullanıcı tanımlı işleçler kümesi boş değilse, bu işlem için aday işleçler kümesi haline gelir. Aksi takdirde, yükseltilmiş formları dahil olmak üzere önceden tanımlanmış ikili operatör operasyon uygulamaları, operasyon için aday operatörler kümesi haline gelir. Belirli bir operatörün önceden tanımlanmış uygulamaları, operatörün açıklamasında belirtilir (§7.8 ila §7.12).
  • §7.5.3 aşırı yük çözümleme kuralları, bağımsız değişken listesine (x, y) göre en iyi operatörü seçmek için aday operatörler kümesine uygulanır ve bu operatör, aşırı yük çözme işleminin sonucu olur. Aşırı yük çözümlemesi tek bir en iyi işleci seçmede başarısız olursa, bir bağlama süresi hatası oluşur.

Öyleyse, sırayla bunu inceleyelim.

X buradaki boş türdür - ya da bu şekilde düşünmek istiyorsanız, hiç bir tür değildir. Herhangi bir aday sağlamıyor. Y, boolherhangi bir kullanıcı tanımlı +operatör sağlamayan . Dolayısıyla ilk adım, kullanıcı tanımlı operatör bulmuyor.

Derleyici daha sonra önceden tanımlanmış ikili operatör + uygulamaları ve bunların kaldırılmış formlarına bakarak ikinci madde işaretine geçer. Bunlar, spesifikasyonun 7.8.4 bölümünde listelenmiştir.

O önceden tanımlanmış operatörler aracılığıyla bakarsak, sadece geçerlidir biridir string operator +(string x, object y). Yani aday setin tek bir girişi vardır. Bu, son madde işaretini çok basit hale getirir ... aşırı yük çözümü, operatörü seçer ve genel bir ifade türü verir string.

İlginç bir nokta, bahsedilmeyen türlerde kullanılabilen başka kullanıcı tanımlı operatörler olsa bile bunun gerçekleşeceğidir. Örneğin:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

Sorun değil, ancak boş bir değişmez için kullanılmaz çünkü derleyici içeri bakmayı bilmiyor Foo. Yalnızca string, spesifikasyonda açıkça listelenen önceden tanımlanmış bir operatör olduğu için dikkate alması gerektiğini bilir . (Aslında, dizge türü tarafından tanımlanan bir operatör değildir ... 1 ) Bu, derlemede başarısız olacağı anlamına gelir:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

Diğer ikinci işlenen türleri, elbette diğer bazı operatörleri kullanacaktır:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 Neden bir string + operatörü olmadığını merak ediyor olabilirsiniz . Bu makul bir soru ve ben sadece cevabında tahmin ediyorum , ancak şu ifadeyi düşünün:

string x = a + b + c + d;

Eğer stringC # derleyicisi hiçbir özel kılıfı vardı bu etkili bir şekilde sona ereceğini:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

Bu, iki gereksiz ara dizi yarattı. Ancak, derleyici içinde özel bir destek olduğundan, aslında yukarıdakileri şu şekilde derleyebilir:

string x = string.Concat(a, b, c, d);

bu, tüm verileri tam olarak bir kez kopyalayarak tam olarak doğru uzunlukta yalnızca tek bir dize oluşturabilir. Güzel.


'genel bir dizge türü ifade etme' Ben buna katılmıyorum. Hata true, dönüştürülememekten kaynaklanıyor string. İfade geçerli olsaydı, tür olurdu string, ancak bu durumda dizeye dönüştürme başarısızlığı tüm ifadeyi bir hata yapar ve dolayısıyla türü yoktur.
leppie

6
@leppie: ifadesi olan geçerli ve bunun tip dizedir. "Var x = null + true;" deneyin - bu derler ve xtiptedir string. İzlerinin burada kullanılan Not string operator+(string, object)- bu çeviriyor booliçin objectdeğil, (ince) string.
Jon Skeet

Teşekkürler Jon, şimdi anla. Spesifikasyonun 2. sürümünde BTW bölüm 14.7.4.
leppie

@leppie: ECMA numaralandırmasında mı? Bu her zaman çok farklı olmuştur :(
Jon Skeet

47
20 dakika içinde. Bütün bunları 20 dakikada yazdı. Kitap falan yazmalı ... oh bekle.
Epaga

44

Bunun nedeni +, C # operatörü bağlama kurallarının devreye girmesidir. +Mevcut operatör setini dikkate alacak ve en iyi aşırı yüklemeyi seçecektir. Bu operatörlerden biri şudur:

string operator +(string x, object y)

Bu aşırı yükleme, ifadedeki bağımsız değişken türleriyle uyumludur null + true. Bu nedenle, operatör olarak seçilir ve esasen ((string)null) + truedeğeri değerlendiren olarak değerlendirilir "True".

C # dil spesifikasyonunun 7.7.4 Bölümü, bu çözünürlükle ilgili ayrıntıları içerir.


1
Üretilen IL'nin doğrudan Concat'ı çağırmaya gittiğini fark ettim. Orada + operatör işlevine bir çağrı göreceğimi düşünüyordum. Bu, optimizasyonu sıralamak mıdır?
quentin-starin

1
@qstarin inanıyorum ki nedeni gerçekten bir operator+için yok string. Bunun yerine, yalnızca derleyicinin zihninde var ve onu basitçe çağrılara çeviriyorstring.Concat
JaredPar

1
@qstarin @JaredPar: Cevabımda buna biraz daha değindim.
Jon Skeet

11

Derleyici, önce boş bir argüman alabilen + () operatörünü aramaya çıkar. Standart değer türlerinden hiçbiri uygun değildir, null onlar için geçerli bir değer değildir. Tek ve tek eşleşme System.String.operator + (), belirsizlik yok.

Bu operatörün 2. argümanı da bir dizedir. Bu kapooey olur, bool'u dizeye çeviremez.


10

İlginç bir şekilde, üretilen şeyi incelemek için Reflector'ı kullanarak aşağıdaki kod:

string b = null + true;
Console.WriteLine(b);

derleyici tarafından buna dönüştürülür:

Console.WriteLine(true);

Bu "optimizasyonun" arkasındaki mantık biraz tuhaf söylemeliyim ve beklediğim operatör seçimiyle uyuşmuyor.

Ayrıca aşağıdaki kod:

var b = null + true; 
var sb = new StringBuilder(b);

dönüştü

string b = true; 
StringBuilder sb = new StringBuilder(b);

nerede string b = true;aslında derleyici tarafından kabul edilmez.


8

nullboş dizeye dönüştürülecek ve bool'den dizeye örtük bir dönüştürücü var, böylece dizeye dönüştürülecek trueve ardından +operatör uygulanacak: bu şöyle: string str = "" + true.ToString ();

Ildasm ile kontrol ederseniz:

string str = null + true;

aşağıdaki gibi:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0

5
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

Çılgın?? Hayır, arkasında bir sebep olmalı.

Biri arar Eric Lippert...


5

Bunun nedeni kolaylıktır (dizeleri birleştirmek yaygın bir görevdir).

BoltClock'un dediği gibi, '+' operatörü sayısal türler, dizeler üzerinde tanımlanır ve kendi türlerimiz için de tanımlanabilir (operatör aşırı yükleme).

Bağımsız değişken türlerinde aşırı yüklenmiş bir '+' operatörü yoksa ve bunlar sayısal türler değilse, derleyici varsayılan olarak dize birleştirme yapar.

Derleyici String.Concat(...), '+' kullanarak birleştirdiğinizde bir çağrı ekler ve Concat uygulaması, kendisine geçirilen her nesne için ToString'i çağırı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.