2008-12-15 104 views
51

假設我有一種方法將某種對象作爲參數。現在說,如果這個方法傳遞一個空參數,這是一個致命的錯誤,應該拋出一個異常。它是值得的,我的代碼是這樣的(記住這是一個簡單的例子):Throwing ArgumentNullException

void someMethod(SomeClass x) 
{ 
    if (x == null){ 
     throw new ArgumentNullException("someMethod received a null argument!"); 
    } 

    x.doSomething(); 
} 

抑或是安全的,我只依靠它扔NullException當它調用x.doSomething() ?

其次,假設的someMethod是一個構造函數和x將不被使用,直到另一種方法被調用。我應該立即拋出異常還是等到需要x並拋出異常呢?

+1

您可能有興趣在通過[10個公用事業C#開發人員應該知道]解釋了Throw.If方法(http://www.codeducky.org/10-utilities-c-developers-should-know-part ))和Throw.IfNull([10 utilities gist on github])(https://gist.github.com/madelson/9177264)。它們允許你更簡潔地編寫你的參數檢查邏輯:`Throw.IfNull(x,「someMethod收到一個空參數!「)` – 2014-05-16 04:26:24

+3

另外值得注意的是,這裏使用的`ArgumentNullException`的構造函數並沒有將完整的消息作爲參數,而是接受`paramName`,它是參數名稱爲null 。請參閱[MSDN的完整定義](http://msdn.microsoft.com/en-us/library/wssa019h(v = vs.110).aspx)。 – 2014-10-08 16:06:14

+0

http://programmers.stackexchange.com/questions/121121/how-do-throw-an-argumentnullexception -help – 2015-04-13 05:27:35

回答

56

我喜歡NullReferenceExceptionArgumentNullException過,不是檢查參數將提供。一般來說,我的首選是在嘗試調用可能爲null的對象上的方法之前始終檢查是否爲空。

如果該方法是一個構造函數,那麼這將取決於幾個不同的因素:是否有同樣的屬性的公共setter和是如何可能的對象將實際使用。如果有公共setter,那麼不通過構造函數提供有效的實例將是合理的,不應該導致例外。

如果沒有公共setter和,可以使用包含對象不參照被注入的對象,你可能要推遲檢查/異常,直到它的使用嘗試。然而,我認爲一般情況下,注入的對象對於實例的功能是必不可少的,因此ArgumentNull異常是完全合理的,因爲實例不能沒有它。

+4

+1,NullReferenceException是壞的 – user7116 2008-12-15 18:34:34

+8

是的,這不是偏好問題ArgumentNullException是正確的,NullReference例外不是。 NullReferenceException用於當空對象是訪問,例如它的一個字段或屬性。 – JamesBrownIsDead 2009-11-10 22:22:15

28

我公司始終遵循的實踐快速失敗。如果你的方法依賴於X並且你知道X可能在null中傳遞,那麼檢查它並立即引發異常,而不是延長失敗點。

2016更新:

現實世界的例子。我強烈建議使用Jetbrains Annotations

[Pure] 
public static object Call([NotNull] Type declaringType, 
          [NotNull] string methodName, 
          [CanBeNull] object instance) 
{ 
    if (declaringType == null) throw new ArgumentNullException(nameof(declaringType)); 
    if (methodName == null) throw new ArgumentNullException(nameof(methodName)); 

使用C#6提供nameof運算符後,Guard語句大大改進。

13

我更喜歡明確的例外,原因如下:

  • 如果方法具有不止一個SomeClass的自變量,它讓你有機會說,這是其中之一(其他一切都可以在調用堆棧) 。
  • 如果你在引用x之前做了一些可能有副作用的事情,該怎麼辦?
3

如果要編制防守,你應該快速失敗。因此,請檢查您的輸入並在代碼開始時輸出錯誤。你應該對你的調用者很好,並給他們提供最具描述性的錯誤信息。

5

最好早點拋出ArgumentNullException,而不要晚點拋出。如果拋出它,您可以提供有關此問題的更多有用信息,而不是NullReferenceException。

4

1-如果您不需要空值,請明確地執行此操作。否則,當其他人查看你的代碼時,他們會認爲接受傳遞一個空值。

2-做得更快,你可以。通過這種方式,您不會在沒有設想的情況下傳播具有Null的「錯誤」行爲。

5

如果您希望輸入不爲null,則應顯式拋出ArgumentNullException。您可能想要編寫一個名爲Guard的類,爲此提供輔助方法。所以,你的代碼將是:

void someMethod(SomeClass x, SomeClass y) 
{ 
    Guard.NotNull(x,"x","someMethod received a null x argument!"); 
    Guard.NotNull(y,"y","someMethod received a null y argument!"); 


    x.doSomething(); 
    y.doSomething(); 
} 

非null方法將做無效支票扔在調用指定的錯誤消息的NullArgumentException。

8

我寧願參數檢查顯式ArgumentNullException也。在元數據

展望:

// 
    // Summary: 
    //  Initializes a new instance of the System.ArgumentNullException class with 
    //  the name of the parameter that causes this exception. 
    // 
    // Parameters: 
    // paramName: 
    //  The name of the parameter that caused the exception. 
    public ArgumentNullException(string paramName); 

可以看到,該字符串應該是參數的名稱,也就是空的,所以給開發商上什麼錯誤的提示。

9

我同意快速失敗的想法 - 但明智地知道爲什麼快速失敗是實用的。考慮下面這個例子:

void someMethod(SomeClass x) 
{  
    x.Property.doSomething(); 
} 

如果依靠NullReferenceException告訴你,什麼是錯,你怎麼會知道什麼是空?堆棧跟蹤只會給你一個行號,而不是哪個引用是空的。在這個例子中,xx.Property可能都是空的,並且事先沒有通過積極檢查快速失敗,您將不知道它是哪一個。

0

我可能會因此而低估,但我認爲完全不同。

如何遵循一個良好的做法,稱爲「永不傳null」並刪除醜陋的異常檢查?

如果參數是一個對象,請不要通過NULL。另外,不要返回NULL。你甚至可以使用Null對象模式來幫助解決這個問題。

如果是可選的,請使用默認值(如果您的語言支持它們)或創建過載。

比醜惡的例外更清潔。

0

您可以使用類似下面的語法來不只是拋出ArgumentNullException,而是將該例外名稱作爲錯誤文本的一部分。例如。;

void SomeMethod(SomeObject someObject) 
{ 
    Throw.IfArgNull(() => someObject); 
    //... do more stuff 
} 

用於引發異常的類是;

public static class Throw 
{ 
    public static void IfArgNull<T>(Expression<Func<T>> arg) 
    { 
     if (arg == null) 
     { 
      throw new ArgumentNullException(nameof(arg), "There is no expression with which to test the object's value."); 
     } 

     // get the variable name of the argument 
     MemberExpression metaData = arg.Body as MemberExpression; 
     if (metaData == null) 
     { 
      throw new ArgumentException("Unable to retrieve the name of the object being tested.", nameof(arg)); 
     } 

     // can the data type be null at all 
     string argName = metaData.Member.Name; 
     Type type = typeof(T); 
     if (type.IsValueType && Nullable.GetUnderlyingType(type) == null) 
     { 
      throw new ArgumentException("The expression does not specify a nullible type.", argName); 
     } 

     // get the value and check for null 
     if (arg.Compile()() == null) 
     { 
      throw new ArgumentNullException(argName); 
     } 
    } 
}