(bu == null) C #!


129

C # 4'te düzeltilen bir hata nedeniyle aşağıdaki program yazdırılır true. (LINQPad'de deneyin)

void Main() { new Derived(); }

class Base {
    public Base(Func<string> valueMaker) { Console.WriteLine(valueMaker()); }
}
class Derived : Base {
    string CheckNull() { return "Am I null? " + (this == null); }
    public Derived() : base(() => CheckNull()) { }
}

Yayın modunda VS2008'de bir InvalidProgramException oluşturur. (Hata Ayıklama modunda, iyi çalışıyor)

VS2010 Beta 2'de derlenmiyor (Beta 1'i denemedim); Zor yoldan öğrendim

this == nullSaf C # ile yapmanın başka bir yolu var mı ?


3
Büyük olasılıkla C # 3.0 derleyicisinde bir hata. C # 4.0'da olması gerektiği gibi çalışır.
Mehrdad Afshari

82
@SLaks: Hatalarla ilgili sorun, bunların bir noktada düzeltilmesini beklemenizdir, bu yüzden onları "yararlı" bulmak muhtemelen akıllıca değildir.
AnthonyWJones

6
Teşekkürler! LINQPad hakkında bilmiyordum. serin!
thorn̈

8
Bu tam olarak ne şekilde yararlıdır?
Allen Rice

6
bu hata nasıl faydalı oldu?
BlackTigerX

Yanıtlar:


73

Bu gözlem, StackOverflow'da bugün erken saatlerde başka bir soruda yayınlandı .

Marc 'ın bu soruya büyük bir cevabı spec (bölüm 7.5.7)' e göre, erişmeye mümkün olmayacaktır gerektiğini gösterir thisbu bağlamda ve # 3.0 derleyici bir hata olduğunu C bunu yapma yeteneğini de. C # 4.0 derleyicisi spesifikasyona göre doğru davranıyor (Beta 1'de bile bu bir derleme zamanı hatasıdır):

§ 7.5.7 Bu erişim

Bir bu erişimli ayrılmış sözcük oluşmaktadır this.

Bu erişimli:

this

Bir bu erişim sadece izin verilen blok örneği yapıcı, bir örnek yöntemi veya bir örneği erişimci.


2
Bu soruda sunulan kodda "bu" anahtar kelimesinin kullanımının neden geçersiz olduğunu anlamıyorum. CheckNull yöntemi, statik olmayan normal bir örnek yöntemidir . Bu yöntemde "this" i kullanmak% 100 geçerlidir ve hatta bunu null ile karşılaştırmak bile geçerlidir. Hata temel başlangıç ​​satırındadır: Bu, örnek sınırlamalı temsilciyi parametre olarak temel ctor'a geçirme girişimidir. Bu, derleyicideki hatadır (sematik kontrollerdeki bir delik): mümkün OLMAMALIDIR. : base(CheckNull())CheckNull statik değilse yazma izniniz yoktur ve benzer şekilde örneğe bağlı bir lambda'yı satır içi yapamazsınız.
quetzalcoatl

4
@quetzalcoatl: thisin CheckNullyöntemine yasaldır. Ne yasal değil ise örtülü bu erişimli içinde () => CheckNull()esasen, () => this.CheckNull()dışarıda çalışıyorsa, bloğun bir örnek yapıcı. Spesifikasyonun bahsettiğim kısmının çoğunlukla thisanahtar kelimenin sözdizimsel yasallığına odaklandığına ve muhtemelen başka bir kısmının bu konuyu daha kesin bir şekilde ele aldığına katılıyorum , ancak spesifikasyonun bu kısmından kavramsal olarak çıkarım yapmak da kolaydır.
Mehrdad Afshari

2
Üzgünüm katılmıyorum. Bunu bildiğim halde (ve bunu yukarıdaki yorumda yazdım) ve siz de biliyorsunuz - sorunun asıl nedenini (kabul edilen) yanıtınızda belirtmediniz. Cevap kabul edildi - öyle görünüyor ki yazar da anladı. Ancak tüm okuyucuların ilk bakışta bir örneğe bağlı lambda'ya karşı statik lambda'yı ilk bakışta tanımak ve bunu 'buna' eşlemek ve yayılan IL ile ilgili sorunları anlamak için lambdalarda parlak ve akıcı olacağından şüpheliyim :) Bu yüzden üç sentimi ekledim. Bunun dışında, siz ve diğerleri tarafından bulunan, analiz edilen ve tarif edilen diğer her şeye katılıyorum :)
quetzalcoatl

24

Hata Ayıklama modu ikilisinin ham derleme (Reflector with no optimizations):

private class Derived : Program.Base
{
    // Methods
    public Derived()
    {
        base..ctor(new Func<string>(Program.Derived.<.ctor>b__0));
        return;
    }

    [CompilerGenerated]
    private static string <.ctor>b__0()
    {
        string CS$1$0000;
        CS$1$0000 = CS$1$0000.CheckNull();
    Label_0009:
        return CS$1$0000;
    }

    private string CheckNull()
    {
        string CS$1$0000;
        CS$1$0000 = "Am I null? " + ((bool) (this == null));
    Label_0017:
        return CS$1$0000;
    }
}

CompilerGenerated yöntemi mantıklı değil; IL'ye (aşağıda) bakarsanız, yöntemi boş bir dizge (!) üzerinde çağırmaktır .

   .locals init (
        [0] string CS$1$0000)
    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: stloc.0 
    L_0007: br.s L_0009
    L_0009: ldloc.0 
    L_000a: ret 

Yayın modunda, yerel değişken optimize edilir, bu nedenle var olmayan bir değişkeni yığına itmeye çalışır.

    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: ret 

(Reflektör, C # dönüştürülürken çöküyor)


DÜZENLEME : Derleyicinin neden yaydığını bilen var mı (Eric Lippert?) ldloc?


11

Ben buna sahiptim! (ve kanıtı da var)

alternatif metin


2
Geç kaldım, kodlamayı bırakmam gerektiğinin bir işaretiydi :) DLR malzemelerimizi IIRC ile hackliyordu.
leppie

'bu' ne olursa olsun bir hata ayıklayıcı görselleştiricisi (DebuggerDisplay) yapın ve sizi boş olan aptal yerine koyun. : D just sayin '

10

Bu bir "hata" değil. Bu, yazı sistemini kötüye kullanıyorsun. thisBir yapıcı içindeki herhangi birine hiçbir zaman geçerli örneğe ( ) bir başvuru iletmemelisiniz .

Ben de temel sınıf oluşturucu içinde sanal bir yöntem çağırarak benzer bir "hata" oluşturabilirim.

Sadece çünkü edebilirsiniz onun bir anlamına gelmez bir şey feci yapmak hata onun tarafından biraz olsun.


14
Bu bir derleyici hatasıdır. Geçersiz IL üretir. (Cevabımı oku)
SLaks

Bağlam statiktir, bu nedenle bu aşamada bir örnek yöntemi referansına izin verilmemelidir.
leppie

10
@Will: Bu bir derleyici hatası. Derleyicinin bu kod parçacığı için geçerli , doğrulanabilir kod üretmesi veya bir hata mesajı vermesi beklenir . Bir derleyici spesifikasyona göre davranmadığında , hatalıdır .
Mehrdad Afshari

2
@ Will # 4: Kodu yazdığım zaman, sonuçları hakkında düşünmemiştim. Sadece VS2010'da derlemeyi durdurduğunda mantıklı olmadığını anladım. -
SLaks

3
Bu arada, yapıcıdaki sanal yöntem çağrısı tamamen geçerli bir işlemdir. Sadece tavsiye edilmez. Bu olabilir mantıklı felaketlerde ama asla bir sebep InvalidProgramException.
Mehrdad Afshari

3

Yanılıyor olabilirim, ama eminim ki eğer nesneniz, geçerli nullolduğu yerde asla bir senaryo olmayacaksa this.

Mesela nasıl ararsınız CheckNull?

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException

3
Yapıcı bağımsız değişkenindeki bir lambda'da. Kod parçacığının tamamını okuyun. (Ve bana inanmıyorsanız deneyin)
SLaks

Katılıyorum, ancak C ++ 'da bir nesnenin yapıcısı içinde bir referansa sahip olmadığına dair bir şey hatırlamama rağmen ve bu durumlarda (this == null) senaryosunun bir yönteme yapılan bir çağrının olup olmadığını kontrol etmek için kullanılıp kullanılmadığını merak ediyorum. bir işaretçiyi "this" olarak göstermeden önce nesnenin yapıcısından yapılır. Yine de, C # 'da bildiğim kadarıyla, Dispose veya sonlandırma yöntemlerinde bile "this" in boş olacağı durumlar olmamalı.
jpierson

Sanırım demek istediğim, fikrinin thistam da boş olma olasılığını karşılıklı olarak dışlamasıdır - bir tür bilgisayar programlamasının bir "Cogito, ergo toplamı". Bu nedenle, ifadeyi kullanma this == nullve gerçeğe dönme arzunuz beni yanlış yönlendiriyor.
Dan Tao

Başka bir deyişle: Kodunuzu okudum; Demek istediğim, ilk başta neyi başarmaya çalıştığınızı sorguluyorum.
Dan Tao

Bu kod basitçe hatayı gösteriyor ve sizin de belirttiğiniz gibi tamamen işe yaramaz. Gerçek kullanışlı kodu görmek için ikinci cevabımı okuyun.
SLaks

-1

Aradığınız şeyin bu olduğundan emin değilim

    public static T CheckForNull<T>(object primary, T Default)
    {
        try
        {
            if (primary != null && !(primary is DBNull))
                return (T)Convert.ChangeType(primary, typeof(T));
            else if (Default.GetType() == typeof(T))
                return Default;
        }
        catch (Exception e)
        {
            throw new Exception("C:CFN.1 - " + e.Message + "Unexpected object type of " + primary.GetType().ToString() + " instead of " + typeof(T).ToString());
        }
        return default(T);
    }

örnek: UserID = CheckForNull (Request.QueryString ["UserID"], 147);


13
Sen tamamen soruyu yanlış anlamış.
SLaks

1
Ben de öyle düşündüm. Yine de deneyeceğimi düşündüm.
Scott and the Dev Team
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.