2013-06-24 43 views
6

對於某些類,理想情況下,我想創建特殊的命名實例,類似於「null」。據我所知,這是不可能的,所以相反,我創建類的靜態實例,用靜態構造函數,與此類似:C#如何創建一個類的特殊實例?

public class Person 
{ 
    public static Person Waldo; // a special well-known instance of Person 
    public string name; 
    static Person() // static constructor 
    { 
     Waldo = new Person("Waldo"); 
    } 
    public Person(string name) 
    { 
     this.name = name; 
    } 
} 

正如你所看到的,Person.Waldo是一個特例我創建的Person類是因爲在我的程序中,有很多其他類可能需要引用這個特殊的知名實例。

實現這種方式的缺點是我不知道任何方法使Person.Waldo的所有屬性都是不可變的,而「正常」Person實例的所有屬性都應該是可變的。每當我意外地有一個人物對象指的是沃爾多,而我不小心不檢查是否指的是沃爾多,那麼我不小心打斷了沃爾多的財產。

有沒有更好的方法,或者甚至有一些其他的替代方法來定義一個特殊的衆所周知的類實例?

我現在唯一知道的解決方案是實現獲取& set訪問器,並檢查每個集合上的「if(this == Waldo)throw new ...」。雖然這有效,但我認爲C#可以比我實現它做得更好。如果只有我可以找到一些C#方法來使沃爾多的所有屬性只讀(除了在靜態構造函數中)。

+0

難道你不能在你的Waldo類中使用訪問器而不使用mutator嗎? – Brian

+0

不是您的問題的直接答案,而是一個可能的方向:http://msdn.microsoft.com/en-us/library/ms750509.aspx和http://stackoverflow.com/questions/263585/immutable-object- pattern-in-c-sharp-what-do-you-think – hatchet

+0

你可以定義一個接口'IPerson',並使'Waldo'成爲一個實現'IPerson'的類,同時'Person'實現它。然後你需要讓'Waldo'不可變,在這裏看看更多的信息你如何讓一個類不可變:http://stackoverflow.com/questions/352471/how-do-i-create-an-immutable-class –

回答

0

感謝您的所有建議 - 以下是解決方案。我需要使字符串變成虛擬的,覆蓋公共派生類中的訪問器,並使用布爾標誌來允許修改。

需要注意的是,「name」是一個引用類型,雖然我阻止了更改「name」引用的內容,但如果它不是字符串之類的東西,比如包含自定義類型的類,修改方法,儘管無法修改對象引用,但仍可以修改對象的內容。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Person myPerson = new Person("Wenda"); 
     System.Console.WriteLine("myPerson is " + myPerson.name);  // Prints "myPerson is Wenda" 

     if (myPerson == Person.Waldo) 
      System.Console.WriteLine("Found Waldo (first attempt)"); // doesn't happen 
     else 
      System.Console.WriteLine("Still trying to find Waldo..."); // Prints "Still trying to find Waldo..." 

     myPerson.name = "Bozo"; 
     System.Console.WriteLine("myPerson is now " + myPerson.name); // Prints "myPerson is now Bozo" 

     myPerson = Person.Waldo; 

     if (myPerson == Person.Waldo) 
      System.Console.WriteLine("Found Waldo (second attempt)"); // Prints "Found Waldo (second attempt)" 

     System.Console.WriteLine("myPerson is " + myPerson.name);  // Prints "myPerson is Waldo" 
     System.Console.WriteLine("Now changing to The Joker...");  // Prints "Now changing to The Joker" 
     try 
     { 
      myPerson.name = "The Joker";        // throws ImmutablePersonModificationAttemptException 
     } 
     catch (ImmutablePersonModificationAttemptException) 
     { 
      System.Console.WriteLine("Failed to change");    // Prints "Failed to change" 
     } 
     System.Console.WriteLine("myPerson is now " + myPerson.name); // Prints "myPerson is now Waldo" 

     Thread.Sleep(int.MaxValue); // keep the console alive long enough for me to see the result. 
    } 
} 
public class Person 
{ 
    public static readonly ImmutablePerson Waldo = new ImmutablePerson("Waldo"); 
    public virtual string name { get; set; } 
    public Person() // empty base constructor required by ImmutablePerson(string) constructor 
    { } 
    public Person(string name) 
    { 
     this.name = name; 
    } 
} 
public class ImmutablePersonModificationAttemptException : Exception 
{ } 
public class ImmutablePerson : Person 
{ 
    private bool allowMutation; 
    protected string _name; 
    public override string name 
    { 
     get 
     { 
      return _name; 
     } 
     set 
     { 
      if (allowMutation) 
       _name = value; 
      else 
       throw new ImmutablePersonModificationAttemptException(); 
     } 
    } 
    public ImmutablePerson(string name) 
     : base() 
    { 
     allowMutation = true; 
     this.name = name; 
     allowMutation = false; 
    } 
} 
4

在Person內部創建一個私有類,繼承Person,ImmutablePerson : Person

將所有屬性設置器鎖定:用例如NotImplementedException覆蓋它們。

你的靜態初始化的人變成這樣: public static readonly Person Waldo = new ImmutablePerson("Waldo");

而且靜態構造函數可以被移除了。

+0

+1因爲我正要說同樣的事情。 –

+0

不幸的是,這是行不通的。你不能公開私人財產,因此不能讓沃爾登公開。 –

1

也許你可以有以下層次:

class Person 
{ 
    protected string _name; 
    public virtual string Name{ 
     get{ 
      return _name; 
     } 
    } 
} 

class EditablePerson:Person 
{ 
    public new string Name{ 
     get{ 
      return _name; 
     } 
     set{ 
      _name=value; 
     } 
    } 
    public Person AsPerson() 
    { 
     //either return this (and simply constrain by interface) 
     //or create an immutable copy 
    } 
} 
+0

這並不奏效 - get&set訪問器需要在基類中定義,以便「正常」實例可以修改。一旦set訪問器在基類中公開,您就不能再更改派生訪問器的「private」或「public」訪問修飾符。 –

0

在我看來,最好是在金都始終返回一個新的實例。這樣原來的Waldo永遠不會改變。但是這會阻止你使用引用相等進行比較,因此你將不得不重寫Equals和GetHashCode。