2009-07-24 328 views
11

Martin Fowler的重構討論創建空的對象,以避免大量的如何C#創建一個空對象

if (myObject == null) 

測試。 什麼是正確的方法來做到這一點?我的嘗試違反了「構造函數中的虛擬成員調用」規則。 這裏是我的嘗試吧:

public class Animal 
{ 
    public virtual string Name { get; set; } 
    public virtual string Species { get; set; } 
    public virtual bool IsNull 
    { 
     get { return false; } 
    } 
} 

public sealed class NullAnimal : Animal 
{ 
    public override string Name 
    { 
     get{ return "NULL"; } 
     set { } 
    } 
    public override string Species 
    { 
     get { return "NULL"; } 
     set { } 
    } 
    public virtual bool IsNull 
    { 
     get { return true; } 
    } 
} 
+3

你試圖解決什麼問題?空引用究竟有什麼問題呢? – spoulson 2009-07-24 15:27:02

+2

http://www.refactoring.com/catalog/introduceNullObject.html – Sisiutl 2009-07-24 15:31:22

+0

您的NullAnimal類中的IsNull屬性應該是重寫,而不是虛擬。 – TGnat 2009-07-24 15:34:55

回答

11

我傾向於同意Wyatt Barnett's answer,因爲在創建這些「空」對象時應該保持克制。也就是說,這樣做有一些很好的理由。不定期的。

我也傾向於同意Supertux's answer,因爲空對象的整個點不需要檢查它是否爲空,所以你應該失去IsNull屬性。如果你真的覺得你需要IsNull屬性,那麼再讀一遍Wyatt的迴應,然後重新考慮。

並感謝你CraigTP for the nice links欲瞭解更多信息。好東西。

現在我將假設在你真實的代碼中,你實際上有一個構造函數試圖設置Name或Species的值(不管你真正的代碼是否可以被調用)。否則,爲什麼你會得到「構造函數中的虛擬成員調用」警告/錯誤?我遇到了幾個類似的問題,當使用新的MyProperty {get;組; }自己的快捷方式(特別是在結構中使用時,並且不要讓我開始關於序列化版本控制)。您的解決方案是不使用快捷方式,而是以傳統方式進行。

public class Animal { 
    protected Animal() { } 

    public Animal(string name, string species) { 
     _Name = name; 
     _Species = species; 
    } 

    public virtual string Name { 
     get { return _Name; } 
     set { _Name = value; } 
    } 
    private string _Name; 

    public virtual string Species { 
     get { return _Species; } 
     set { _Species = value; } 
    } 
    private string _Species; 
} 

public sealed class NullAnimal : Animal { 
    public override string Name { 
     get { return String.Empty; } 
     set { } 
    } 
    public override string Species { 
     get { return String.Empty; } 
     set { } 
    } 
} 

這解決了在構造函數中設置虛擬屬性的問題。相反,您正在設置您的私人字段值(如果您使用快捷方式,則無法引用該字段)。要獲得額外的功勞,請編譯這兩種方法,並使用Reflector查看生成的程序集。

我越用{get;組; }捷徑,我越不喜歡它。

22

去查查那個有趣的概念,如爲DBNull,造成疼痛的量,想想如果這其實是一個好主意。 Protip:如果你經常檢查空引用,你可能應該重新考慮一下API來幫助排除靠近堆棧頂部的空對象。

Protip II:當出現意想不到的null時,有東西會引發異常,實際上很好,並且很花哨。如果你的空位不應該爲零,那麼事情會變得繁榮。

+7

+1。空圖案是一種反模式IMO。拋出不需要的空值並在允許時檢查空值。 – Randolpho 2009-07-24 15:35:36

3

空對象模式的要點是它不需要空檢查來防止崩潰或錯誤。

例如,如果您嘗試對Species屬性執行操作並且該操作爲空 - 則會導致錯誤。

所以,你不應該需要一個isNull方法,只返回在吸氣的東西,不會導致應用程序崩潰/錯誤如:

public class Animal 
{ 
    public virtual string Name { get; set; } 
    public virtual string Species { get; set; } 
} 

public sealed class NullAnimal : Animal 
{ 
    public override string Name 
    { 
     get{ return string.Empty; } 
     set { ; } 
    } 
    public override string Species 
    { 
     get { return string.Empty; } 
     set { ; } 
    } 
} 
2

你只使用這種方法,如果是適當的。你的動物對象的例子可能不是一個好例子,因爲它沒有提供一個適合你使用這種方法的情況。例如:

Animal animal = new Animal(); 

if (animal.tail == null) 
{ 
    //do nothing because wagging a tail that doesn't exist may crash the program 
} 
else 
{ 
    animal.wagTail(); 
} 

在這個例子中,你應該建立動物對象,因此,如果動物沒有尾巴,它能夠成功地處理了鶺鴒()命令沒有崩潰。

Class Animal 
{ 
    Tail tail; 

    void wagTail() 
    { 
     if (this.tail == null) 
     { 
      //do nothing 
     } 
     else 
     { 
      this.tail.doTheWag(); 
     } 
    } 
} 

現在你不需要做一個空檢查,但可以叫animal.wagTail()無論動物是否有尾巴與否。

0

我想在這裏提一些有趣的細節。看看你的班級。它有任何邏輯嗎?這不是一個類,它是一個數據結構。你所要做的是將空對象模式應用於不適用的東西。數據結構比類型更接近值類型。有沒有空檢查可以解決您的問題。 空對象模式不是你應該始終遵循的。空對象模式是一種可以用來避免Liskov的替代原則違反的事情,用來表示一個什麼也不做的類,因爲null不適合替代類,因爲它是一個值,但不是類。 但是值的類型和數據結構有所不同。空值是價值!所以在這種情況下,空檢查是正確的。