Yana true
bir dize türüdür nasıl null + true
bir 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?
Yana true
bir dize türüdür nasıl null + true
bir 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?
Yanıtlar:
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, bool
herhangi 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 string
C # 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.
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.
x
tiptedir string
. İzlerinin burada kullanılan Not string operator+(string, object)
- bu çeviriyor bool
için object
değil, (ince) string
.
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) + true
değ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.
operator+
için yok string
. Bunun yerine, yalnızca derleyicinin zihninde var ve onu basitçe çağrılara çeviriyorstring.Concat
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.
İ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.
null
boş 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 true
ve 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
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
...
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.