(this == null) en C #!
-
05-07-2019 - |
Question
En raison d'un bogue corrigé dans C # 4, le programme suivant imprime true
. (Essayez-le dans 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()) { }
}
Dans VS2008 en mode Release, une exception InvalidProgramException est levée. (En mode débogage, cela fonctionne bien)
Dans la version bêta 2 de VS2010, la compilation n’est pas effectuée (je n’ai pas essayé la version bêta 1); J'ai appris que la dure
Existe-t-il un autre moyen de créer this == null
en C # pur?
La solution
Cette observation a été publiée dans StackOverflow à autre question plus tôt aujourd'hui.
La traduction bonne réponse à cette question indique que, conformément à la spécification (section 7.5.7), vous ne devriez pas pouvoir accéder à this
dans ce contexte et la possibilité de le faire dans un compilateur C # 3.0 est un bogue. Le compilateur C # 4.0 se comporte correctement conformément à la spécification (même en version bêta 1, il s'agit d'une erreur de compilation):
& # 167; 7.5.7 Cet accès
Un this-access est constitué du mot réservé
this
.this-access:
this
Un this-access n'est autorisé que dans le bloc d'un d'un constructeur d'instance, d'une méthode d'instance ou d'un accesseur d'instance.
Autres conseils
La décompilation brute (réflecteur sans optimisations) du fichier binaire en mode débogage est la suivante:
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<*>;
}
}
La méthode CompilerGenerated n'a pas de sens; si vous regardez l'IL (ci-dessous), il appelle la méthode sur une chaîne (!).
<*>En mode Release, la variable locale est optimisée, elle essaie donc de placer une variable non existante sur la pile.
<*>(Le réflecteur tombe en panne lorsqu'il est converti en C #)
EDIT : Quelqu'un (Eric Lippert?) sait-il pourquoi le compilateur émet le ldloc
?
J'ai eu ça! (et j'ai aussi des preuves)
Ce n'est pas un "bug". C'est vous abuser du système de types. Vous n'êtes jamais censé transmettre une référence à l'instance actuelle ( this
) à quiconque se trouvant dans un constructeur.
Je pourrais créer un "bogue" similaire. en appelant également une méthode virtuelle dans le constructeur de la classe de base.
Ce n’est pas parce que vous pouvez faire quelque chose de mauvais que cela soit un bogue que vous vous sentiez mordu.
Je peux me tromper, mais je suis presque sûr que si votre objet est null
, il ne se produira jamais de scénario où this
s'applique.
Par exemple, comment appelleriez-vous CheckNull
?
Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
Vous ne savez pas si c'est ce que vous recherchez
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);
}
exemple: UserID = CheckForNull (Request.QueryString [" UserID &]; 147);