(this == null) in C #!
-
05-07-2019 - |
Domanda
A causa di un errore corretto in C # 4, il seguente programma stampa true
. (Provalo in 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()) { }
}
In VS2008 in modalità Release, genera un InvalidProgramException. (In modalità Debug, funziona benissimo)
In VS2010 Beta 2, non viene compilato (non ho provato Beta 1); L'ho imparato a mie spese
Esiste un altro modo per rendere this == null
in C # puro?
Soluzione
Questa osservazione è stata pubblicata su StackOverflow in altro domanda prima di oggi.
Marc 's un'ottima risposta a questa domanda indica che, secondo le specifiche (sezione 7.5.7), non dovresti essere in grado di accedere a this
in quel contesto e la possibilità di farlo nel compilatore C # 3.0 è un bug. Il compilatore C # 4.0 si comporta correttamente in base alle specifiche (anche in Beta 1, si tratta di un errore di tempo di compilazione):
§ 7.5.7 Questo accesso
Un questo accesso è costituito dalla parola riservata
this
.questo accesso:
this
Un questo accesso è consentito solo nel blocco di un costruttore di istanze, di un metodo di istanza o di un accessor di istanza.
Altri suggerimenti
La decompilazione grezza (Reflector senza ottimizzazioni) del binario della modalità Debug è:
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 .locals init (
[0] string CS$1 L_0000: ldloc.0
L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
L_0006: ret
)
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
;
CS$1<*> = CS$1<*>.CheckNull();
Label_0009:
return CS$1<*>;
}
private string CheckNull()
{
string CS$1<*>;
CS$1<*> = "Am I null? " + ((bool) (this == null));
Label_0017:
return CS$1<*>;
}
}
Il metodo CompilerGenerated non ha senso; se guardi IL (sotto), sta chiamando il metodo su una stringa (!) nulla.!
<*>In modalità Rilascio, la variabile locale è ottimizzata, quindi cerca di inserire una variabile inesistente nello stack.
<*>(Reflector si arresta in modo anomalo quando lo si trasforma in C #)
MODIFICA : qualcuno (Eric Lippert?) sa perché il compilatore emette il ldloc
?
L'ho avuto! (e anche le prove)
Questo non è un bug " bug " ;. Stai abusando del sistema dei tipi. Non devi mai passare un riferimento all'istanza corrente ( this
) a chiunque all'interno di un costruttore.
Potrei creare un simile "errore" chiamando un metodo virtuale anche nel costruttore della classe base.
Solo perché puoi fare qualcosa di male non significa che si tratta di un bug quando ne rimani morso.
Potrei sbagliarmi, ma sono abbastanza sicuro che il tuo oggetto sia null
non ci sarà mai uno scenario in cui questo
si applica.
Ad esempio, come chiameresti CheckNull
?
Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
Non sono sicuro se questo è quello che stai cercando
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);
}
esempio: UserID = CheckForNull (Request.QueryString [" UserID "], 147);