web-dev-qa-db-fra.com

En C #, que se passe-t-il lorsque vous appelez une méthode d'extension sur un objet null?

La méthode est-elle appelée avec une valeur null ou donne-t-elle une exception de référence null?

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?

Si tel est le cas, je n'aurai jamais besoin de vérifier mon paramètre 'this' pour null?

302
tpower

Cela fonctionnera bien (sans exception). Les méthodes d’extension n’utilisent pas d’appels virtuels (c’est-à-dire qu’elles utilisent l’instruction "call", pas "callvirt"). Par conséquent, il n’ya pas de vérification de null à moins que vous ne l’écriviez vous-même dans la méthode d’extension. Ceci est utile dans quelques cas:

public static bool IsNullOrEmpty(this string value)
{
    return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
        where T : class
{
    if(obj == null) throw new ArgumentNullException(parameterName);
}

etc

Fondamentalement, les appels à des appels statiques sont très littéraux - c.-à-d.

string s = ...
if(s.IsNullOrEmpty()) {...}

devient:

string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

où il n'y a évidemment pas de chèque nul.

361
Marc Gravell

Ajout à la réponse correcte de Marc Gravell.

Vous pouvez recevoir un avertissement du compilateur s'il est évident que l'argument this est null:

default(string).MyExtension();

Fonctionne bien au moment de l'exécution, mais produit l'avertissement "Expression will always cause a System.NullReferenceException, because the default value of string is null".

48

Comme vous l'avez déjà découvert, étant donné que les méthodes d'extension sont simplement des méthodes statiques glorifiées, elles seront appelées avec les références null passées, sans qu'un jeton NullReferenceException ne soit jeté. Mais, puisqu'elles ressemblent à des méthodes d'instance pour l'appelant, elles devraient également se comporter En tant que tel. La plupart du temps, vous devriez alors vérifier le paramètre this et émettre une exception s’il s’agit de null. C'est bien de ne pas le faire si la méthode s'occupe explicitement de null valeurs et que son nom l'indique correctement, comme dans les exemples ci-dessous:

public static class StringNullExtensions { 
  public static bool IsNullOrEmpty(this string s) { 
    return string.IsNullOrEmpty(s); 
  } 
  public static bool IsNullOrBlank(this string s) { 
    return s == null || s.Trim().Length == 0; 
  } 
}

J'ai aussi écrit n article de blog à ce sujet il y a quelque temps.

20
Jordão

Un null sera passé à la méthode d'extension.

Si la méthode tente d'accéder à l'objet sans la vérifier si elle est nulle, alors oui, elle lève une exception.

Un gars ici a écrit les méthodes d'extension "IsNull" et "IsNotNull" qui vérifient si la référence est null ou non. Personnellement, je pense que c'est une aberration et que je n'aurais pas dû voir la lumière du jour, mais c'est parfaitement valide c #.

16
Binary Worrier

Comme d'autres l'ont fait remarquer, l'appel d'une méthode d'extension sur une référence null entraîne la nullité de cet argument et que rien d'autre ne se produira. Cela donne à penser à utiliser des méthodes d'extension pour écrire des clauses de protection.

Vous pouvez lire cet article pour des exemples: Comment réduire la complexité cyclomatic: clause de garde La version courte est la suivante:

public static class StringExtensions
{
    public static void AssertNonEmpty(this string value, string paramName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("Value must be a non-empty string.", paramName);
    }
}

C'est la méthode d'extension de la classe string qui peut être appelée sur une référence null:

((string)null).AssertNonEmpty("null");

L'appel fonctionne correctement uniquement parce que l'exécution appelle correctement la méthode d'extension sur une référence null. Ensuite, vous pouvez utiliser cette méthode d'extension pour implémenter des clauses de garde sans syntaxe désordonnée:

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }
6
Zoran Horvat

La méthode d'extension est statique, donc si vous ne faites rien à ce MyObject, cela ne devrait pas être un problème, un test rapide devrait le vérifier :)

3
Fredrik Leijon