2009-04-02 7 views
5

Ho creato una classe con proprietà che hanno valori predefiniti. Ad un certo punto della vita dell'oggetto, mi piacerebbe "ripristinare" le proprietà dell'oggetto indietro a ciò che erano quando l'istanza è stata istanziata. Per esempio, diciamo che questa è stata la classe:Come reinizializzare o ripristinare le proprietà di una classe?

public class Truck { 
    public string Name = "Super Truck"; 
    public int Tires = 4; 

    public Truck() { } 

    public void ResetTruck() { 
     // Do something here to "reset" the object 
    } 
} 

Poi ad un certo punto, dopo che i Name e Tires proprietà sono state cambiate, il metodo ResetTruck() potrebbe essere chiamato e le proprietà verrebbe ripristinato torna a "Super Truck" e 4, rispettivamente.

Qual è il modo migliore per reimpostare le proprietà sui valori predefiniti iniziali hardcoded?

risposta

9

Si può avere l'inizializzazione in un metodo, invece di inlining con la dichiarazione. Poi hanno il costruttore e ripristinare il metodo chiamare il metodo di inizializzazione:

public class Truck { 
    public string Name; 
    public int Tires; 

    public Truck() { 
     Init(); 
    } 

    public void ResetTruck() { 
     Init(); 
    } 

    private void Init() { 
     Name = "Super Truck"; 
     Tires = 4; 
    } 
} 

Un altro modo non è quello di avere un metodo di ripristino a tutti. Basta creare una nuova istanza.

+1

In una certa misura, questo è in realtà quello che stavo facendo, ma mi stavo chiedendo se ci fosse un modo migliore. –

0

Probabilmente dovresti salvare i valori nei campi privati, in modo che possano essere ripristinati in seguito. Forse qualcosa di simile:

public class Truck 
{ 
    private static const string defaultName = "Super Truck"; 
    private static const int defaultTires = 4; 

    // Use properties for public members (not public fields) 
    public string Name { get; set; } 
    public int Tires { get; set; } 

    public Truck() 
    { 
     Name = defaultName; 
     Tires = defaultTires; 
    } 

    public void ResetTruck() 
    { 
     Name = defaultName; 
     Tires = defaultTires; 
    } 

} 
+2

Se hai preso questo approccio, quindi mi piacerebbe dichiaro i valori di default come static const. Dopotutto li stai usando come costanti. – AaronLS

+0

Vero, cambierò quello –

2

Se avete fatto il vostro inizializzazione in un metodo di ripristino si può essere pronti per partire:

public class Truck { 
    public string Name; 
    public int Tires; 

    public Truck() { 
    ResetTruck(); 
    } 

    public void ResetTruck() { 
     Name = "Super Truck"; 
     Tires = 4; 
    } 
} 
3

A meno che la creazione dell'oggetto non è molto costoso (e Reset non è per qualche motivo). Non vedo alcun motivo per implementare un metodo di ripristino speciale. Perché non devi solo creare una nuova istanza con uno stato predefinito utilizzabile.

Qual è lo scopo del riutilizzo dell'istanza?

+0

Perché l'oggetto stesso è ciò che farà il ripristino. Non dipende dal codice che istanzia l'oggetto per avviare il reset. –

+4

@Dylan: A me sembra che la classe stia facendo troppo. Dai un'occhiata al Principio di Responsabilità Unica per maggiori informazioni. –

0

Stai essenzialmente cercando il modello State design

+0

Siamo spiacenti ma NO! Questo non ha bisogno del sovraccarico di un modello statale quando ci sono soluzioni più pulite, anche se potresti usarlo se ti senti come un masochista. :) – arviman

+0

Elaborare ... –

+0

Quello che sto ottenendo è che * potrebbe * usare lo schema di stato qui per ottenere questa funzionalità, ma non è rilevante. Ti consiglierei di leggere il modello poiché sembra che tu abbia incasinato le "macchine di stato". Lo schema di stato consente di modificare il comportamento di un attributo di un contesto in base allo stato. Qui, tutto quello che stai facendo è tornare allo stato iniziale. Non c'è alcun cambiamento nel comportamento necessario. – arviman

3

riflessione è tuo amico. È possibile creare un metodo di supporto per utilizzare Activator.CreateInstance() per impostare il valore predefinito di Tipi di valore e "null" per i tipi di riferimento, ma perché occuparsi dell'impostazione di null su un SetValue di PropertyInfo farà lo stesso.

Type type = this.GetType(); 
    PropertyInfo[] properties = type.GetProperties(); 
    for (int i = 0; i < properties.Length; ++i) 
     properties[i].SetValue(this, null); //trick that actually defaults value types too. 

Per estendere questo per il vostro scopo, hanno soci privati:

//key - property name, value - what you want to assign 
Dictionary<string, object> _propertyValues= new Dictionary<string, object>(); 
List<string> _ignorePropertiesToReset = new List<string>(){"foo", "bar"}; 

impostare i valori nel costruttore:

public Truck() { 
    PropertyInfo[] properties = type.GetProperties(); 

    //exclude properties you don't want to reset, put the rest in the dictionary 
    for (int i = 0; i < properties.Length; ++i){ 
     if (!_ignorePropertiesToReset.Contains(properties[i].Name)) 
      _propertyValues.Add(properties[i].Name, properties[i].GetValue(this)); 
    } 
} 

reset in un secondo momento:

public void Reset() { 
    PropertyInfo[] properties = type.GetProperties(); 
    for (int i = 0; i < properties.Length; ++i){ 
     //if dictionary has property name, use it to set the property 
     properties[i].SetValue(this, _propertyValues.ContainsKey(properties[i].Name) ? _propertyValues[properties[i].Name] : null);  
    } 
} 
+0

Preferisco usare 'FormatterServices.GetUninitializedObject()' su 'Activator.CreateInstance()'. I costruttori vengono saltati, tutti i campi sono garantiti come null/default. –

2

Focalizzazione della separazione di c oncerns (come Brian accennato nei commenti), un'altra alternativa potrebbe essere quella di aggiungere un tipo TruckProperties (si potrebbe anche aggiungere i valori di default per il suo costruttore):

public class TruckProperties 
{ 
    public string Name 
    { 
     get; 
     set; 
    } 

    public int Tires 
    { 
     get; 
     set; 
    } 

    public TruckProperties() 
    { 
     this.Name = "Super Truck"; 
     this.Tires = 4; 
    } 

    public TruckProperties(string name, int tires) 
    { 
     this.Name = name; 
     this.Tires = tires; 
    } 
} 

Dentro la classe Truck, tutto quello che vi fare è gestire un'istanza del tipo TruckProperties e lasciare che esegua il ripristino.

public class Truck 
{ 
    private TruckProperties properties = new TruckProperties(); 

    public Truck() 
    { 
    } 

    public string Name 
    { 
     get 
     { 
      return this.properties.Name; 
     } 
     set 
     { 
      this.properties.Name = value; 
     } 
    } 

    public int Tires 
    { 
     get 
     { 
      return this.properties.Tires; 
     } 
     set 
     { 
      this.properties.Tires = value; 
     }   
    } 

    public void ResetTruck() 
    { 
     this.properties = new TruckProperties(); 
    } 
} 

Questo certamente può essere un sacco di overhead (indesiderati) per una classe così semplice, ma in un progetto più ampio/più complesso potrebbe essere vantaggioso.

Questa è la cosa delle "migliori" pratiche ... un sacco di volte, non c'è una pallottola d'argento, ma solo raccomandazioni da prendere con scetticismo e il tuo miglior giudizio su ciò che si applica a te in un caso particolare.

0

Se si desidera uno "stato" passato specifico del proprio oggetto, è possibile creare un particolare punto di salvataggio da restituire ogni volta che lo si desidera. Ciò ti consente anche di avere uno stato diverso di backup per ogni istanza che crei. Se la tua classe ha molte proprietà che sono in costante cambiamento, questa potrebbe essere la tua soluzione.

public class Truck 
{ 
    private string _Name = "Super truck"; 
    private int _Tires = 4; 

    public string Name 
    { 
     get { return _Name; } 
     set { _Name = value; } 
    } 
    public int Tires 
    { 
     get { return _Tires; } 
     set { _Tires = value; } 
    } 

    private Truck SavePoint; 

    public static Truck CreateWithSavePoint(string Name, int Tires) 
    { 
     Truck obj = new Truck(); 
     obj.Name = Name; 
     obj.Tires = Tires; 
     obj.Save(); 
     return obj; 
    } 

    public Truck() { } 

    public void Save() 
    { 
     SavePoint = (Truck)this.MemberwiseClone(); 
    } 

    public void ResetTruck() 
    { 
     Type type = this.GetType(); 
     PropertyInfo[] properties = type.GetProperties(); 
     for (int i = 0; i < properties.Count(); ++i) 
      properties[i].SetValue(this, properties[i].GetValue(SavePoint)); 
    } 
} 
0

Ho risolto un problema simile con la riflessione. È possibile utilizzare source.GetType().GetProperties() per ottenere un elenco di tutte le proprietà che appartengono all'oggetto.

Anche se, questa non è sempre una soluzione completa. Se il tuo oggetto implementa diverse interfacce, otterrai anche tutte quelle proprietà con la tua chiamata di riflessione.

Così ho scritto questa semplice funzione che ci dà più controllo su quali proprietà ci interessa reimpostare.

public static void ClearProperties(object source, List<Type> InterfaceList = null, Type SearchType = null) 
    { 


     // Set Interfaces[] array size accordingly. (Will be size of our passed InterfaceList, or 1 if InterfaceList is not passed.) 
     Type[] Interfaces = new Type[InterfaceList == null ? 1 : InterfaceList.Count]; 

     // If our InterfaceList was not set, get all public properties. 
     if (InterfaceList == null) 
      Interfaces[0] = source.GetType(); 
     else // Otherwise, get only the public properties from our passed InterfaceList 
      for (int i = 0; i < InterfaceList.Count; i++) 
       Interfaces[i] = source.GetType().GetInterface(InterfaceList[i].Name); 


     IEnumerable<PropertyInfo> propertyList = Enumerable.Empty<PropertyInfo>(); 
     foreach (Type face in Interfaces) 
     { 
      if (face != null) 
      { 
       // If our SearchType is null, just get all properties that are not already empty 
       if (SearchType == null) 
        propertyList = face.GetProperties().Where(prop => prop != null); 
       else // Otherwise, get all properties that match our SearchType 
        propertyList = face.GetProperties().Where(prop => prop.PropertyType == SearchType); 

       // Reset each property 
       foreach (var property in propertyList) 
       { 
        if (property.CanRead && property.CanWrite) 
         property.SetValue(source, null, new object[] { }); 
       } 
      } 
      else 
      { 
       // Throw an error or a warning, depends how strict you want to be I guess. 
       Debug.Log("Warning: Passed interface does not belong to object."); 
       //throw new Exception("Warning: Passed interface does not belong to object."); 
      } 
     } 
    } 

Ed è l'uso:

// Clears all properties in object 
ClearProperties(Obj); 
// Clears all properties in object from MyInterface1 & MyInterface2 
ClearProperties(Obj, new List<Type>(){ typeof(MyInterface1), typeof(MyInterface2)}); 
// Clears all integer properties in object from MyInterface1 & MyInterface2 
ClearProperties(Obj, new List<Type>(){ typeof(MyInterface1), typeof(MyInterface2)}, typeof(int)); 
// Clears all integer properties in object 
ClearProperties(Obj,null,typeof(int));