Z powodu błędu, który został naprawiony w C # 4, drukuje następujący program true
. (Wypróbuj w LINQPad)
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()) { }
}
W VS2008 w trybie wydania zgłasza wyjątek InvalidProgramException. (W trybie debugowania działa dobrze)
W VS2010 Beta 2 nie kompiluje się (nie próbowałem Beta 1); Nauczyłem się tego na własnej skórze
Czy istnieje inny sposób tworzenia this == null
w czystym języku C #?
Odpowiedzi:
Ta obserwacja została opublikowana w StackOverflow w innym pytaniu w dniu dzisiejszym.
Marc „s wielki odpowiedź na to pytanie wskazuje, że zgodnie z specyfikacją (rozdział 7.5.7), nie powinny być w stanie uzyskać dostęp
this
w tym kontekście oraz możliwość to zrobić w C # 3.0 kompilator jest błąd. Kompilator C # 4.0 zachowuje się poprawnie zgodnie ze specyfikacją (nawet w wersji Beta 1 jest to błąd czasu kompilacji):źródło
: base(CheckNull())
jeśli CheckNull nie jest statyczny, i podobnie nie powinieneś być w stanie wstawić lambdy związanej z instancją.this
wCheckNull
metodzie jest legalne. Co nie jest zgodne z prawem jest niejawna tego dostępu w() => CheckNull()
istocie() => this.CheckNull()
, która działa poza blokiem z konstruktora instancji. Zgadzam się, że część specyfikacji, którą cytuję, koncentruje się głównie na składniowej legalnościthis
słowa kluczowego i prawdopodobnie inna część odnosi się do tej kwestii bardziej precyzyjnie, ale również z tej części specyfikacji łatwo jest koncepcyjnie ekstrapolować.Surowa dekompilacja (Reflector bez optymalizacji) pliku binarnego trybu debugowania to:
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; } }
Metoda CompilerGenerated nie ma sensu; jeśli spojrzysz na IL (poniżej), wywołuje ona metodę na łańcuchu pustym (!).
.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
W trybie wydania zmienna lokalna jest zoptymalizowana, więc próbuje umieścić nieistniejącą zmienną na stosie.
L_0000: ldloc.0 L_0001: call instance string CompilerBug.Program/Derived::CheckNull() L_0006: ret
(Reflektor ulega awarii podczas przekształcania go w C #)
EDYCJA : Czy ktoś (Eric Lippert?) Wie, dlaczego kompilator emituje
ldloc
?źródło
Miałem to! (i mam też dowód)
źródło
To nie jest „błąd”. To ty nadużywasz systemu typów. Nigdy nie należy przekazywać odwołania do bieżącej instancji (
this
) nikomu w konstruktorze.Mógłbym stworzyć podobny „błąd”, wywołując metodę wirtualną w konstruktorze klasy bazowej.
Tylko dlatego, że możesz zrobić coś złego, nie oznacza, że jest to błąd, gdy zostaniesz przez to ugryziony.
źródło
InvalidProgramException
.Mogę się mylić, ale jestem prawie pewien, że jeśli twoim celem jest
null
to, nigdy nie będzie scenariusza, którythis
miałby zastosowanie.Na przykład, jak byś zadzwonił
CheckNull
?Derived derived = null; Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
źródło
this
wyklucza się wzajemnie z możliwością bycia zerowym - coś w rodzaju „Cogito, ergo sum” programowania komputerowego. Dlatego twoje pragnienie, by użyć tego wyrażeniathis == null
i kiedykolwiek przywrócić mu prawdę, wydaje mi się błędne.Nie jestem pewien, czy tego właśnie szukasz
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); }
przykład: UserID = CheckForNull (Request.QueryString ["ID użytkownika"], 147);
źródło