2013-06-24 11 views
6

Dla niektórych klas, chciałbym utworzyć specjalne nazwane instancje, podobne do "null". O ile wiem, to nie jest możliwe, więc zamiast tworzyć statyczne instancje klasy, ze statycznym konstruktorze, podobny do tego:C# Jak tworzyć specjalne wystąpienia klasy?

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

Jak widać, Person.Waldo jest szczególnym przypadkiem klasa Person, którą utworzyłem, ponieważ w moim programie istnieje wiele innych klas, które mogą chcieć odwołać się do tej specjalnej, dobrze znanej instancji.

Minusem implementacji w ten sposób jest to, że nie znam żadnego sposobu, aby wszystkie właściwości Person.Waldo były niezmienne, podczas gdy wszystkie właściwości "normalnej" instancji Person powinny być zmienne. Ilekroć przypadkowo mam przedmiot Person odnoszący się do Waldo, a ja niedbale nie sprawdzam, czy to odnosi się do Waldo, to przypadkowo włamuję się do właściwości Waldo.

Czy istnieje lepszy sposób, a nawet kilka dodatkowych alternatywnych sposobów definiowania specjalnych znanych instancji klasy?

Jedyne rozwiązanie, jakie znam teraz, to implementacja akcesoriów get & i zaznaczenie "jeśli (to == Waldo) wyrzuć nowe ..." na każdym zestawie. Podczas gdy to działa, zakładam, że C# może wykonać lepszą pracę niż ja przy jego wdrażaniu. Jeśli tylko znajdę sposób C#, aby wszystkie właściwości Waldo były odczytane (z wyjątkiem konstruktora statycznego).

+0

Nie możesz po prostu użyć akcesora w swojej klasie Waldo i nie użyć mutatora? – Brian

+0

Nie jest to bezpośrednia odpowiedź na Twoje pytanie, ale możliwy kierunek: http://msdn.microsoft.com/en-us/library/ms750509.aspx i http://stdnowe%C5%9Broowy.com/questions/263585/immutable-object- pattern-in-c-sharp-what-do-you-think – hatchet

+0

Możesz zdefiniować interfejs 'IPerson' i uczynić' Waldo' klasą implementującą 'IPerson', a także' Person' ją implementującą.Następnie trzeba dokonać 'Waldo' niezmienne, zajrzyj tutaj po więcej informacji jak zrobić klasę niezmienna: http://stackoverflow.com/questions/352471/how-do-i-create-an-immutable-class –

Odpowiedz

0

Dzięki wszystkim sugestiom - oto rozwiązanie. Musiałem utworzyć ciąg wirtualny, nadpisać akcesory w publicznej klasie pochodnej i użyć flagi bool, aby umożliwić modyfikację.

Należy pamiętać, że "nazwa" jest typem odniesienia, i chociaż uniemożliwiłem zmianę nazwy "nazwa", jeśli było to coś innego niż ciąg znaków, np. Klasa zawierająca Metodę modyfikacji można nadal modyfikować zawartość obiektu, mimo że nie udało się zmodyfikować odniesienia do obiektu.

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

Wykonaj prywatną klasę wewnątrz Osoby, która dziedziczy Osobę, ImmutablePerson : Person.

Ustaw wszystkie blokady ustawień właściwości: zastąp je wyjątkiem NotImplementedException na przykład.

statycznej inicjalizacji osoba staje się w ten sposób: public static readonly Person Waldo = new ImmutablePerson("Waldo");

i statycznych konstruktor może zostać usunięta, too.

+0

+1, ponieważ chciałem powiedzieć to samo. –

+0

Niestety to nie działa. Nie możesz publicznie udostępnić prywatnej nieruchomości, dlatego nie możesz udostępnić Waldo publicznie. –

1

Może masz następującą hierarchię:

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

To nie zadziałało - ustawienia get & set muszą być zdefiniowane w klasie bazowej, aby "normalna" instancja była modyfikowalna. A gdy ustawiony akcesor zostanie upubliczniony w klasie bazowej, nie można wtedy zmienić "prywatnego" lub "publicznego" modyfikatora dostępu wyprowadzonego akcesora. –

0

moim zdaniem najlepiej jest zawsze wrócić nowe wystąpienie na Waldo. W ten sposób oryginalny Waldo nigdy nie zostanie zmieniony. Ale to uniemożliwi ci użycie porównania odniesienia dla porównania, dlatego będziesz musiał przesłonić Equals i GetHashCode.

Powiązane problemy