2009-12-09 8 views
27

I tienen el siguiente método de extensión:¿Es posible refactorizar este método de extensión?

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

y esto es un ejemplo de su uso ....

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

funciona al 100% bien.

Pero, no me gusta cómo tengo que proporcionar el nombre de la variable, solo para ayudar a mi mensaje de excepción.

Me preguntaba si es posible refactorizar el método de extensión, por lo que se podría llamar así ...

qwerty.ThrowIfArgumentIsNull(); 

y automáticamente se da cuenta de que el nombre de la variable es 'QWERTY' y por lo tanto lo usa como el valor de ArgumentNullException.

¿Posible? Supongo que la reflexión podría hacer esto?

+0

ver también http://stackoverflow.com/questions/869610/c-resolving-a-parameter-name-at-runtime/869629#869629 –

+4

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

+0

Creo que esta es una solución muy difícil para un problema muy simple ... Si está usando Visual Studio, puede use fragmentos de código para que esto sea muy fácil;) –

Respuesta

34

No, no puedes hacer esto. Sería bueno, pero no es posible sin la participación de algún tipo de AOP. Estoy seguro de PostSharp puede hacer un buen trabajo, es de esperar utilizando atributos, y en los contratos de código que sólo sería:

Contract.Requires(qwerty != null); 

Lo ideal sería un atributo PostSharp que genera los contratos código de llamada - y voy a jugar con eso en algún momento, pero hasta entonces, el método de extensión que tienes es el mejor enfoque que he encontrado ...

(Si alguna vez trato con el enfoque PostSharp + Contratos de código, sin duda alguna blog sobre él, por cierto ... Mono Cecil podría hacerlo razonablemente fácil también.)

EDITAR: Para ampliar la respuesta de Laurent, podría tener potencialmente :

new { qwerty }.CheckNotNull(); 

Y si tuviera una gran cantidad de parámetros no anulable, usted podría tener:

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

Esto tendría que utilizar la reflexión para averiguar las propiedades. Hay formas en que podría evitar hacer la reflexión sobre cada acceso, crear un delegado para cada propiedad y, por lo general, hacerlo galopante. Puedo investigar esto para una publicación de blog ... pero es un poco asqueroso, y prefiero la idea de poder simplemente atribuir los parámetros ...

EDIT: Código implementado, y blog post debidamente realizado. Ick, pero divertido

+0

Gracias Jon por la pronta respuesta :) –

+1

y +1 por una publicación en el blog :) También podría ver la respuesta de Laurnet también ... –

+4

Leí tu publicación en el blog, y honestamente no tienes que pasar por todos ese alboroto. Simplemente implemente un método 'ThrowIfNull()' sin params y permita que el desarrollador que realiza el seguimiento de la pila suba un poco la pila para descubrir qué argumento es nulo. Solo un pensamiento :) – RCIX

3

En una palabra: no.

Al método de extensión se le pasa un valor. No tiene idea de dónde proviene el valor o qué identificador ha elegido el que llama para referirse a él como.

1

Yo recomendaría que más bien lo siguiente:

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

El uso de los genéricos en este caso no parece añadir ningún valor. Pero en cuanto a su pregunta original, no creo que eso sea posible.

+2

El uso de genéricos permite que el método excluya los tipos de valores. Tenga en cuenta el 'where T: class' – Greg

1

Ver también ArgumentNullException and refactoring para una completa soluciones a lo largo de las mismas líneas que la respuesta .

¿Qué hay de:

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

definen a continuación ThrowIfArgumentIsNull como

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

lo siento, no tengo el tiempo para precisar los detalles o proporcionar el código completo en la actualidad.

+0

No necesita la parte' return' en el lambda. –

2

Me resulta más fácil hacer esto con un fragmento de código.

En su ejemplo, puedo escribir tna<tab>qwerty<enter>.

Aquí es el fragmento:

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

me gusta Enforce del Lokad Shared Libraries.

sintaxis básica:

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

Esto lanzará una excepción con el nombre del parámetro y el tipo si alguno de los argumentos es nulo.

Cuestiones relacionadas