2009-12-09 10 views
27

Ich habe folgende Erweiterungsmethode:Kann diese Erweiterungsmethode umgestaltet werden?

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

und dies ist ein Beispiel für die Verwendung ....

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

arbeitet zu 100% in Ordnung.

Aber ich mag nicht, wie ich den Namen der Variablen bereitstellen muss, nur um meine Ausnahmebedingung zu helfen.

ich mich gefragt, ob es möglich ist, die Erweiterung Methode Refactoring, so könnte es so ...

qwerty.ThrowIfArgumentIsNull(); 

und automatisch herausfindet aufgerufen werden, dass der Name der Variablen ‚QWERTY‘ und deshalb verwendet dies als Wert für die ArgumentNullException.

Möglich? Ich nehme an, Reflexion könnte das tun?

+0

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

+4

Überprüfen Sie http://msmvps.com/blogs/jon_skeet/archive/ 2009/12/09/quot-magic-quot-null-argument-testing.aspx - gebloggt :) –

+0

Ich denke, das ist eine sehr schwierige Lösung für ein sehr einfaches Problem ... Wenn Sie Visual Studio verwenden, können Sie Verwenden Sie Code-Snippets, um dies sehr einfach zu machen;) –

Antwort

34

Nein, das geht nicht. Es wäre schön, aber es ist nicht möglich ohne eine Art von AOP zu beteiligen. Ich bin sicher, dass Postsharp kann einen guten Job machen, hoffentlich Attribute verwenden und in dem Code Verträge wäre es nur sein:

Contract.Requires(qwerty != null); 

Im Idealfall würde ich ein Postsharp Attribut wie der der Code Verträge nennen erzeugt - und ich werde spielen Sie irgendwann damit herum - aber bis dahin ist die Erweiterungsmethode die beste Methode, die ich gefunden habe ...

(Wenn ich jemals den Ansatz PostSharp + Code Contracts versuche, werde ich sicherlich blog über es, btw ... Mono Cecil könnte es auch relativ einfach machen.)

EDIT: Um Laurent Antwort zu erweitern, könnten Sie möglicherweise haben Dies hätte zur Reflexion verwenden, um herauszufinden, die Eigenschaften

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

:

new { qwerty }.CheckNotNull(); 

Und Sie konnten haben, wenn Sie viele Nicht-Nullable-Parameter hatte. Es gibt Möglichkeiten, die Reflektion bei jedem Zugriff zu vermeiden, indem Sie für jede Eigenschaft einen Delegierten erstellen und diesen im Allgemeinen in Schwung bringen. Ich kann dies für einen Blog-Post untersuchen ... aber es ist etwas eklig, und ich bevorzuge die Idee, in der Lage, nur die Parameter ...

EDIT: Code implementiert, und blog post gebührend gemacht. Ick, aber Spaß

+0

Danke Jon für die prompte Antwort :) –

+1

und +1 für einen Blog-Post :) Ich könnte auch Laurnet's Antwort auch überprüfen .... –

+4

Ich las Ihren Blog-Post, und Sie müssen ehrlich gesagt nicht durch alle gehen dieser Hurlabaloo. Implementieren Sie einfach eine 'ThrowIfNull()' Methode ohne Parameter und lassen Sie den Stack-Trace toting-Entwickler den Stack ein wenig nach oben gehen, um herauszufinden, welches Argument NULL ist. Nur ein Gedanke :) – RCIX

3

In einem Wort: nein.

Der Erweiterungsmethode wird ein Wert übergeben. Es hat keine Ahnung, woher der Wert kommt oder welche Kennung der Aufrufer gewählt haben könnte, um darauf zu verweisen.

1

würde ich empfehlen, dass Sie eher Folgendes tun:

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

die Verwendung von Generika in diesem Fall scheint keinen Wert hinzuzufügen. Aber zu Ihrer ursprünglichen Frage glaube ich nicht, dass das möglich ist.

+2

Die Verwendung von Generika ermöglicht der Methode, Werttypen auszuschließen. Notieren Sie die 'wo T: Klasse' – Greg

1

Siehe auch ArgumentNullException and refactoring für eine komplette Lösungen auf der gleichen Linie wie die Antwort.

Was:

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

dann ThrowIfArgumentIsNull als

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

leider definiere ich im Detail oder geben Sie den vollständigen Code derzeit nicht über die Zeit zu füllen.

+0

Sie brauchen nicht den' return' Teil im Lambda. –

2

Ich finde es am einfachsten, dies mit einem Code-Snippet zu tun.

In Ihrem Beispiel kann ich tna<tab>qwerty<enter> eingeben. Hier

ist das Snippet:

<?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

Ich mag Enforce vom Lokad Shared Libraries.

Basic-Syntax:

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

Dies wird eine Ausnahme mit dem Parameternamen und die Art, wenn eines der Argumente ist null werfen.

Verwandte Themen