29 votes

this == null dans la méthode d'instance .NET - pourquoi est-ce possible?

J'ai toujours pensé que c'est impossible this nulle à l'intérieur de la méthode d'instance corps. Suivant programme simple montre qu'il est possible. Ce n'est pas documentée de comportement?

class Foo
{
    public void Bar()
    {
        Debug.Assert(this == null);
    }
}

public static void Test()
{            
    var action = (Action)Delegate.CreateDelegate(typeof (Action), null, typeof(Foo).GetMethod("Bar"));
    action();
}

Mise à JOUR

Je suis d'accord avec les réponses en disant que c'est la façon dont cette méthode est documenté. Cependant, je ne comprends pas vraiment ce comportement. Surtout parce que c'est pas comment C# est conçu.

Nous avons eu un rapport de quelqu'un (probablement l'un de la .NET les groupes à l'aide de C# (pensé qu'il n'était pas encore nommé C# à l'époque)) qui avait écrit le code qui appelle une méthode sur un pointeur null, mais ils n'ont pas obtenez une exception parce que la méthode n'a pas accès à tous les champs (c'est à dire "ce" a été nulle, mais rien dans la méthode utilisée). Cette méthode, puis d'appeler une autre méthode qui n'utiliser ce point et a jeté un exception, et un peu de casse-tête s'ensuivit. Après ils ont compris hors, ils nous ont envoyé une note à ce sujet. Nous avons pensé que le fait de pouvoir appeler une méthode sur une instance null est une peu bizarre. Peter Golde fait quelques tests pour voir quel est l'impact de la perf était toujours à l'aide de callvirt, et il était assez petit, que nous avons décidé pour effectuer le changement.

http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

21voto

Mark Sowul Points 4249

Parce que vous êtes de passage null dans la firstArgument de Delegate.CreateDelegate

Donc, vous êtes à l'appel d'une méthode d'instance sur un objet null.

http://msdn.microsoft.com/en-us/library/74x8f551.aspx

Si firstArgument est une référence null et est une méthode d'instance, le résultat dépend de la signature du délégué du type et de méthode:

Si la signature de type inclut explicitement le caché premier paramètre de la méthode, le délégué est dit représenter un ouvert méthode d'instance. Lorsque le délégué est appelé, le premier argument en la liste d'arguments est transmis à l'instance masquée paramètre de la méthode.

Si les signatures de méthode et le type de match (qui est, de tous les paramètres les types sont compatibles), puis le délégué est dit "fermé" sur un référence null. Invoquer le délégué, c'est comme appeler une instance la méthode sur une instance null, ce qui n'est pas particulièrement utile de le n'.

11voto

Alois Kraus Points 6179

Bien sûr, vous pouvez appeler une méthode si vous utilisez l'appel IL d'instruction ou le délégué de l'approche. Vous allez définir ce piège que si vous essayez d'accéder aux champs des membres qui vous donnera l'exception NullReferenceException vous n'avez chercher.

essayez

 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }

La BCL ne contiennent même explicite ce == null vérifie à l'aide de débogage pour les langues qui n'utilisent pas callvirt (comme C#) de tous les temps. Voir cette question pour de plus amples infos.

La classe String, par exemple, a à de telles vérifications. Il n'y a rien de mystérieux, sauf que vous ne verrez pas la nécessité pour eux dans des langages tels que C#.

// Determines whether two strings match. 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
public override bool Equals(Object obj)
{
    //this is necessary to guard against reverse-pinvokes and
    //other callers who do not use the callvirt instruction
    if (this == null)
        throw new NullReferenceException();

    String str = obj as String;
    if (str == null) 
        return false;

    if (Object.ReferenceEquals(this, obj)) 
        return true;

    return EqualsHelper(this, str);
}

5voto

Kevin Anderson Points 2910

Essayez la documentation de Delegate.CreateDelegate() sur msdn .

Vous appelez "manuellement" tout, et donc au lieu de passer une instance pour le pointeur this , vous passez null. Cela peut donc arriver, mais vous devez vraiment essayer très fort.

4voto

Jirka Hanika Points 8266

this est une référence, donc il n'y a pas de problème avec ses null du point de vue du type de système.

Vous demandez peut-être pourquoi NullReferenceException n'a pas été levée. La liste complète de circonstances, quand la CLR jette cette exception, c'est documenté. Votre cas n'est pas répertorié. Oui, c' est un callvirt, mais à l' Delegate.Invoke (voir ici) plutôt que d' Bar, et donc le this de référence est en fait de votre non-null délégué!

Le comportement que vous voyez est une intéressante implementational conséquence pour le CLR. Un délégué a Target de la propriété (correspond à votre this de référence) qui est assez fréquemment null, à savoir lorsque le délégué est statique (imaginez Bar être statique). Maintenant, il y a, naturellement, d'une salle de sauvegarde champ de la propriété, appelée _target. N' _target contenir une valeur null pour un délégué statique? Non, il n'est pas. Il contient une référence au délégué lui-même. Pourquoi ne pas nulle? Parce que null est une cible légitime d'un délégué comme votre exemple montre et CLR n'est pas deux saveurs d'un null pointeur de distinguer le délégué statique en quelque sorte.

Ce bit de trivium démontre que les délégués, null objectifs des méthodes d'instance sont pas après coup. Vous pouvez toujours poser l'ultime question: mais pourquoi ils ont dû être pris en charge?

Le début du CLR avait un plan ambitieux de devenir, entre autres, la plate-forme de choix, même pour l'assermentation des développeurs C++, un objectif qui était approché d'abord avec la gestion du C++ et du C++/CLI. Certains trop difficile fonctionnalités de langage ont été omitten, mais il n'y a rien de vraiment difficile à soutenir les méthodes d'instance de l'exécution sans une instance, ce qui est parfaitement normal en C++. Y compris la prise en charge des délégués.

L'ultime réponse est donc: parce que le C# et le CLR sont deux mondes différents.

Plus bonne lecture et encore mieux la lecture de montrer la conception permettant null instances montre ses traces, même dans de très naturel C# le contexte syntaxique.

0voto

Sando Points 322

il s'agit d'une référence en lecture seule dans les classes C #. En conséquence et comme prévu, cela peut être utilisé comme toutes les autres références (en mode lecture seule) ...

 this == null // readonly - possible
this = new this() // write - not possible
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X