2009-12-09 15 views
27

Ho il seguente metodo di estensione:È possibile refactoring questo metodo di estensione?

public static void ThrowIfArgumentIsNull<T>(this T value, string argument) 
    where T : class 
{ 
    if (value == null) 
    { 
     throw new ArgumentNullException(argument); 
    } 
} 

e questo è un esempio del suo utilizzo ....

// Note: I've poorly named the argument, on purpose, for this question. 
public void Save(Category qwerty) 
{ 
    qwerty.ThrowIfArgumentIsNull("qwerty"); 
    .... 
} 

funziona al 100% bene.

Ma, non mi piace come devo fornire il nome della variabile, solo per aiutare il mio messaggio di eccezione.

Mi chiedevo se è possibile refactoring il metodo di estensione, in modo che si potrebbe definire come questo ...

qwerty.ThrowIfArgumentIsNull(); 

e figure automaticamente che il nome della variabile è 'QWERTY' e, pertanto, lo usa come valore per ArgumentNullException.

Possibile? Sto assumendo che la riflessione potrebbe fare questo?

+0

vedere anche http://stackoverflow.com/questions/869610/c-resolving-a-parameter-name-at-runtime/869629#869629 –

+4

Controllare http://msmvps.com/blogs/jon_skeet/archive/ 2009/12/09/quot-magic-quot-null-argument-testing.aspx - bloggato :) –

+0

Penso che questa sia una soluzione molto difficile per un problema molto semplice ... Se stai usando Visual Studio, puoi usa i frammenti di codice per semplificare l'operazione;) –

risposta

34

No, non puoi farlo. Sarebbe bello, ma non è possibile senza una sorta di AOP coinvolti. Sono sicuro che PostSharp può fare un bel lavoro, si spera con gli attributi, e nel codice dei contratti sarebbe solo:

Contract.Requires(qwerty != null); 

Idealmente mi piacerebbe un attributo PostSharp che genera i Contratti codice di chiamata - e sarò a un certo punto - ma fino ad allora, il metodo di estensione che hai è il miglior approccio che ho trovato ...

(Se mai provassi l'approccio PostSharp + Code Contracts, farò sicuramente blog su di esso, btw ... Mono Cecil potrebbe renderlo ragionevolmente facile anche.)

EDIT: Per ampliare la risposta di Laurent, si potrebbe potenzialmente avere :

new { qwerty }.CheckNotNull(); 

E se hai avuto un sacco di parametri non nullable, si potrebbe avere:

new { qwerty, uiop, asdfg }.CheckNotNull(); 

Questo avrebbe dovuto usare riflessione per capire le proprietà. Ci sono modi in cui è possibile evitare di fare la riflessione su ogni accesso, costruendo un delegato per ogni proprietà e generalmente rendendolo sfrigolante. Potrei indagare su questo per un post sul blog ... ma è piuttosto icky, e preferisco l'idea di essere in grado di attribuire semplicemente i parametri ...

MODIFICA: codice implementato, e blog post debitamente compilato. Ick, ma divertente

+0

Grazie Jon per la pronta risposta :) –

+1

e +1 per un post sul blog :) potrei anche dare un'occhiata alla risposta di Laurnet .... –

+4

Ho letto il tuo post sul blog, e onestamente non devi passare attraverso tutti quel hullabaloo. Implementa semplicemente un metodo 'ThrowIfNull()' senza parametri e consenti allo sviluppatore di stack-trace di far girare lo stack un po 'per capire quale argomento è nullo. Solo un pensiero :) – RCIX

3

In una parola: no.

Il metodo di estensione è passato un valore. Non ha idea da dove viene il valore o quale identificatore il chiamante può aver scelto di riferirsi ad esso come.

1

mi sento di raccomandare che invece effettuare le seguenti operazioni:

public static void ThrowIfArgumentIsNull(this object value, string argument) 
{ 
    if (value == null) 
    { 
     throw new ArgumentNullException(argument); 
    } 
} 

Utilizzando farmaci generici in questo caso non sembra aggiungere alcun valore. Ma per quanto riguarda la tua domanda iniziale, non penso che sia possibile.

+2

L'utilizzo dei generici consente al metodo di escludere i tipi di valore. Nota 'dove T: class' – Greg

1

Vedi anche ArgumentNullException and refactoring per un completo soluzioni lungo le stesse linee come la risposta .

Che dire:

public void Save(Category qwerty) 
{ 
    ThrowIfArgumentIsNull(() => return qwerty); 
    qwerty.ThrowIfArgumentIsNull("qwerty");  
    // .... 
} 

quindi definire ThrowIfArgumentIsNull come

public static void ThrowIfArgumentIsNull(Expression<Func<object>> test) 
{ 
    if (test.Compile()() == null) 
    { 
     // take the expression apart to find the name of the argument 
    } 
} 

dispiace non ho il tempo di compilare dettaglio o fornire il codice completo al momento.

+0

Non hai bisogno della parte' return' nella lambda. –

2

Trovo che sia più semplice farlo utilizzando uno snippet di codice.

Nell'esempio, è possibile digitare tna<tab>qwerty<enter>.

Ecco il frammento:

<?xml version="1.0" encoding="utf-8" ?> 
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
     <Header> 
       <Title>Check for null arguments</Title> 
       <Shortcut>tna</Shortcut> 
       <Description>Code snippet for throw new ArgumentNullException</Description> 
       <Author>SLaks</Author> 
       <SnippetTypes> 
         <SnippetType>Expansion</SnippetType> 
         <SnippetType>SurroundsWith</SnippetType> 
       </SnippetTypes> 
     </Header> 
     <Snippet> 
       <Declarations> 
         <Literal> 
           <ID>Parameter</ID> 
           <ToolTip>Paremeter to check for null</ToolTip> 
           <Default>value</Default> 
         </Literal> 
       </Declarations> 
       <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$"); 
     $end$]]> 
       </Code> 
     </Snippet> 
    </CodeSnippet> 
</CodeSnippets> 
1

mi piace Enforce dal Lokad Shared Libraries.

sintassi di base:

Enforce.Arguments(() => controller,() => viewManager,() => workspace); 

Questo sarà un'eccezione con il nome del parametro e il tipo se uno degli argomenti è nullo.

Problemi correlati