2013-06-24 13 views
6

Per alcune classi, idealmente, mi piacerebbe creare istanze con nome speciale, simili a "null". Per quanto ne so, questo non è possibile, così, invece, ho creare istanze statici della classe, con un costruttore statico, simile a questo:C# Come creare istanze speciali di una classe?

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

Come si può vedere, Person.Waldo è un caso speciale di la classe Person, che ho creato perché nel mio programma, ci sono molte altre classi che potrebbero voler fare riferimento a questa speciale istanza ben nota.

Lo svantaggio di implementare questo modo è che non conosco alcun modo per rendere immutabili tutte le proprietà di Person.Waldo, mentre tutte le proprietà di un'istanza di Persona "normale" dovrebbero essere modificabili. Ogni volta che accidentalmente ho un oggetto Person che si riferisce a Waldo e incautamente non controllo per vedere se si riferisce a Waldo, poi accidentalmente clobber le proprietà di Waldo.

C'è un modo migliore, o anche qualche altro modo alternativo, per definire le istanze ben note di una classe?

L'unica soluzione che conosco adesso è quella di implementare gli accessi di set & e selezionare "if (this == Waldo) per lanciare nuovi ..." su ogni set. Mentre funziona, suppongo che C# possa fare un lavoro migliore di me per implementarlo. Se solo riesco a trovare un modo C# per rendere tutte le proprietà di Waldo in sola lettura (tranne durante il costruttore statico.)

+0

Non potresti semplicemente usare l'accessor nella tua classe Waldo e non usare il mutatore? – Brian

+0

Non una risposta diretta alla tua domanda, ma una possibile direzione: http://msdn.microsoft.com/en-us/library/ms750509.aspx e http://stackoverflow.com/questions/263585/immutable-object- pattern-in-c-sharp-what-do-think- – hatchet

+0

Si potrebbe definire un'interfaccia 'IPerson' e rendere' Waldo' una classe che implementa 'IPerson', anche' Person' la implementa.Quindi è necessario rendere 'Waldo' immutabile, dare un'occhiata qui per ulteriori informazioni su come rendere una classe immutabile: http://stackoverflow.com/questions/352471/how-do-i-create-an-immutable-class –

risposta

0

Grazie a tutti i vostri suggerimenti - Ecco la soluzione. Avevo bisogno di creare stringhe virtuali, sovrascrivere gli accessor in una classe derivata pubblica e usare un flag bool per consentire la modifica.

È importante notare che "nome" è un tipo di riferimento e anche se ho impedito di cambiare il "nome" a cui si riferisce, se si tratta di qualcosa di diverso da una stringa, come una classe che contiene un metodo di modifica, potrebbe ancora essere possibile modificare il contenuto dell'oggetto, nonostante abbia impedito la modifica del riferimento all'oggetto.

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

Creare una classe privata all'interno della Persona che eredita la Persona, ImmutablePerson : Person.

Rendi tutti i setter della proprietà bloccati: sostituiscili con NotImplementedException per esempio.

tuo inizializzazione persona statica diventa questa: il costruttore public static readonly Person Waldo = new ImmutablePerson("Waldo");

E statica può essere allontanato, anche.

+0

+1 perché stavo per dire la stessa cosa. –

+0

Sfortunatamente, questo non funziona. Non è possibile rendere pubblica una proprietà privata, quindi non rendere disponibile Waldo pubblicamente. –

1

Forse si potrebbe avere la seguente gerarchia:

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

Questo non ha funzionato abbastanza - gli accessi get & set devono essere definiti nella classe base, in modo che l'istanza "normale" sia modificabile. E una volta che l'accessor set viene reso pubblico nella classe base, non è possibile modificare il modificatore di accesso "privato" o "pubblico" dell'accessorio derivato. –

0

A mio parere il migliore è quello di restituire sempre una nuova istanza su Waldo. In questo modo il Waldo originale non sarà mai cambiato. Ma questo ti impedirà di usare l'uguaglianza di riferimento per il confronto, quindi dovrai eseguire l'override di Equals e GetHashCode.

Problemi correlati