是否可以重构此扩展方法?

时间:2009-12-09 11:25:03

标签: .net extension-methods

我有以下扩展方法:

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

这是它的用法示例......

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

100%罚款。

但是,我不喜欢我必须提供变量的名称,只是为了帮助我的异常消息。

我想知道是否可以重构扩展方法,所以它可以这样调用......

qwerty.ThrowIfArgumentIsNull();

并自动指出变量的名称是'qwerty',因此将其用作ArgumentNullException的值。

可能?我假设反思可以做到这一点?

6 个答案:

答案 0 :(得分:34)

不,你不能这样做。这会很好,但如果没有某种AOP参与,这是不可能的。我确信PostSharp可以做得很好,希望使用属性,而在Code Contracts中它只是:

Contract.Requires(qwerty != null);

理想情况下,我想要一个生成Code Contracts调用的PostSharp属性 - 我会在某个时候使用它 - 但在那之前,你所获得的扩展方法是我发现的最佳方法。 ..

(如果我尝试过PostSharp + Code Contracts方法,我肯定会在博客上发表文章,顺便说一句...... Mono Cecil也可能会让它变得相当简单。)

编辑:要扩展Laurent的答案,您可能会:

new { qwerty }.CheckNotNull();

如果你有很多不可为空的参数,你可以:

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

这必须使用反射来计算属性。有些方法可以避免对每个访问进行反射,为每个属性构建一个委托,并且通常会使其变得眩目。我可能会对博客文章进行调查......但它有些蠢,而且我更喜欢能够将参数归因于......

编辑:代码已实施,blog post正式制作。 Ick,但很有趣

答案 1 :(得分:3)

总之:不。

扩展方法传递一个值。它不知道值来自何处或调用者可能选择将其称为什么标识符。

答案 2 :(得分:2)

我发现使用代码段最容易做到这一点。

在您的示例中,我可以输入tna<tab>qwerty<enter>

以下是摘录:

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

答案 3 :(得分:1)

我建议你做以下事情:

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

在这种情况下使用泛型似乎没有添加任何值。但至于你原来的问题,我认为这是不可能的。

答案 4 :(得分:1)

  

另见ArgumentNullException and refactoring完整版   与...相同的解决方案   答案。

怎么样:

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

然后将ThrowIfArgumentIsNull定义为

public static void ThrowIfArgumentIsNull(Expression<Func<object>> test)
{
   if (test.Compile()() == null)
   {
      // take the expression apart to find the name of the argument
   }
}
抱歉,我没有时间填写详细信息或提供目前的完整代码。

答案 5 :(得分:1)

我喜欢Enforce中的Lokad Shared Libraries

基本语法:

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

如果任何参数为null,这将引发参数名称和类型的异常。