2009-12-09 50 views
27

我有以下擴展方法:是否可以重構此擴展方法?

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的值。

可能嗎?我假設反思可以做到這一點?

+0

另請參閱http://stackoverflow.com/questions/869610/c-resolving-a-parameter-name-at-runtime/869629#869629 – 2009-12-09 13:34:16

+4

檢查http://msmvps.com/blogs/jon_skeet/archive/ 2009/12/09/quot; magic-quot; null-argument-testing.aspx - 博客:) – 2009-12-09 18:10:16

+0

我認爲這是一個非常簡單的問題非常困難的解決方案...如果你使用Visual Studio,你可以使用代碼片段使這非常容易;) – 2009-12-09 19:22:15

回答

34

不,你不能這樣做。這很好,但如果沒有某種AOP參與,這是不可能的。我敢肯定,PostSharp可以做了很好的工作,希望使用屬性,並在代碼契約這純粹是:

Contract.Requires(qwerty != null); 

理想情況下,我想它生成代碼契約調用PostSharp屬性 - 我會玩的,在某些時候 - 但在那之前,你已經得到了擴展方法是我發現最好的辦法......

(如果我曾經嘗試PostSharp +代碼契約的方式,我一定會博客關於它,順便說一句... Mono Cecil可能會使它也相當容易。)

編輯:爲了擴大Laurent的答案,你可能有可能:

new { qwerty }.CheckNotNull(); 

如果你有很多的不可爲空的參數,你可以有:

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

這將不得不使用反射來制定出特性。有幾種方法可以避免在每次訪問時進行反思,爲每個屬性構建一個委託並通常使其變爲whizzy。我可能會調查這個博客文章...但它有點噁心,我更喜歡能夠只是歸因於參數的想法...

編輯:代碼實施,並blog post正式作出。伊克,但有趣

+0

感謝喬恩的提示答案:) – 2009-12-09 11:32:32

+1

和+1的博客文章:)我也可能檢查出Laurnet的答案也.... – 2009-12-09 13:27:18

+4

我讀了你的博客文章,你真的不必經歷所有的那個hullabaloo。只需實現一個沒有參數的'ThrowIfNull()'方法,讓棧開發人員走上堆棧一點,找出哪個參數爲空。只是一個想法:) – RCIX 2009-12-10 07:58:25

3

一句話:沒有。

擴展方法傳遞一個值。它不知道這個值來自哪裏,或者調用者可能選擇什麼標識符來表示它。

1

我建議你,而做到以下幾點:

​​3210

在這種情況下使用泛型似乎並沒有增加任何價值。但至於你原來的問題,我不認爲這是可能的。

+2

使用泛型允許該方法排除值類型。注意'where T:class' – Greg 2009-12-10 01:26:00

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

抱歉,我沒有時間去填寫細節或目前提供的完整代碼。

+0

你不需要lambda中的'return'部分。 – 2009-12-09 19:34:53

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

我喜歡從Lokad Shared LibrariesEnforce

基本語法:

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

這將引發與參數名稱和類型的異常,如果任何參數爲空。

相關問題