2009-05-11 18 views
241

Il metodo viene chiamato con un valore nullo o fornisce un'eccezione di riferimento null?In C#, cosa succede quando si chiama un metodo di estensione su un oggetto nullo?

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

Se questo è il caso, non avrò mai bisogno di controllare il mio parametro "questo" per null?

+37

Non fa mai male discutere di queste cose. – tpower

+18

Felice che la domanda sia qui, mi ha salvato un po 'di tempo. – famousgarkin

+6

Grazie per la domanda. Alcuni dei commenti su questo sito stanno diventando troppo negativi in ​​quanto tpower dice che non fa mai male discutere. – nixon

risposta

304

Che funzionerà correttamente (nessuna eccezione). I metodi di estensione non usano chiamate virtuali (ad esempio, usano "call" l'istruzione, non "callvirt"), quindi non c'è controllo nullo a meno che non lo si scriva autonomamente nel metodo di estensione. Questo è in realtà utile, in alcuni casi:

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); 
} 

ecc

Fondamentalmente, le chiamate alle chiamate statiche sono molto letterale - cioè

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

diventa:

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

dove ci ovviamente non è un assegno nulla.

+1

+1, nota aggiuntiva nella mia risposta. –

+1

Marc, stai parlando di chiamate "virtuali", ma lo stesso vale per le chiamate non virtuali su metodi di istanza. Penso che la parola "virtuale" qui sia fuori luogo. –

+2

@Konrad: dipende dal contesto. Il compilatore C# usa solitamente callvirt anche per metodi non virtuali, precisamente per ottenere un controllo nullo. –

2

L'extensionmethod è statico, quindi se non per nulla al presente MyObject non dovrebbe essere un problema, un rapido test deve verificare che :)

12

A nulla viene passato al metodo di estensione .

Se il metodo tenta di accedere all'oggetto senza controllo è nullo, quindi sì, genererà un'eccezione.

Un utente ha scritto "IsNull" e "IsNotNull" i metodi di estensione che controllano il riferimento passato o no. Personalmente penso che sia un'aberrazione e non dovrebbe aver visto la luce del giorno, ma è perfettamente C#.

+0

Sono d'accordo con te, questo potrebbe portare solo alla confusione. – Trap

+13

Infatti, per me è come chiedere a un cadavere "Sei vivo" e ottenere una risposta di "no". Un cadavere non può rispondere a nessuna domanda, né dovresti essere in grado di "chiamare" un metodo su un oggetto nullo. –

+2

haha! buon esempio! :) – Trap

45

Aggiunta alla risposta corretta di Marc Gravell.

Si potrebbe ottenere un avvertimento da parte del compilatore se è ovvio che il presente argomento è nullo:

default(string).MyExtension(); 

Funziona bene in fase di esecuzione, ma produce l'avviso "Expression will always cause a System.NullReferenceException, because the default value of string is null".

+20

Perché dovrebbe avvertire "causa sempre una System.NullReferenceException". Quando infatti non lo sarà mai? – tpower

+13

verificato - glitch del compilatore interessante ;-p +1 per l'ingegno. –

+2

@tpower: sicuramente perché questo controllo non è mai stato aggiornato per gestire correttamente i metodi di estensione. L'ho trovato quando ho provato a chiamare un metodo di estensione che in realtà richiede solo il tipo di argomento, ma non ho avuto un'istanza. Ora devo chiamare un metodo statico che è molto più lungo. –

-1

Ci sono poche regole d'oro quando vuoi essere leggibile e verticale.

  • una pena di dire dal Eiffel dice il codice specifico incapsulato in un metodo dovrebbe funzionare contro alcuni input, che il codice è praticabile se siano soddisfatte alcune condizioni preliminari e assicurare una produzione attesa

Nel vostro caso - DesignByContract è danneggiato ... eseguirai qualche logica su un'istanza nulla.

+0

Bertrand Meyer è un ragazzo così intelligente – Trap

12

Come hai già scoperto, dal momento che i metodi di estensione sono metodi statici semplicemente glorificato, perché saranno chiamati con null riferimenti passati in, senza NullReferenceException essere gettati. Ma, dal momento che sembrano metodi di istanza per il chiamante, dovrebbero anche comportarsi come .Dovresti quindi, la maggior parte delle volte, controllare il parametro this e lanciare un'eccezione se è null. Va bene non fare questo se il metodo richiede esplicitamente cura di null valori e il suo nome lo indica debitamente, come negli esempi che seguono:

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; 
    } 
} 

Ho anche scritto a blog post su questo qualche tempo fa.

4

Come altri hanno sottolineato, chiamare un metodo di estensione su riferimento null fa sì che questo argomento sia nullo e non accadrà altro speciale. Ciò solleva l'idea di utilizzare i metodi di estensione per scrivere clausole di salvaguardia.

si può leggere questo articolo per gli esempi: How to Reduce Cyclomatic Complexity: Guard Clause Versione breve è questa:

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); 
    } 
} 

Questo è il metodo di estensione classe string che può essere chiamato il riferimento null:

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

Le opere di chiamata bene solo perché runtime chiamerà con successo il metodo di estensione su riferimento null. Quindi è possibile utilizzare questo metodo di estensione per implementare clausole di protezione senza sintassi disordinata:

public IRegisteredUser RegisterUser(string userName, string referrerName) 
    { 

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

     ... 

    } 
Problemi correlati