2009-04-02 16 views
5

Utworzono klasę z właściwościami, które mają wartości domyślne. W pewnym momencie życia obiektu, chciałbym "zresetować" właściwości obiektu z powrotem do tego, czym były, gdy obiekt został utworzony. Na przykład, powiedzmy, że to była klasa:Jak ponownie zainicjować lub zresetować właściwości klasy?

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

    public Truck() { } 

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

Wtedy w pewnym momencie, po Name i Tires właściwości zostały zmienione, metoda ResetTruck() można nazwać i właściwości zostaną przywrócone do „Super Truck” i odpowiednio 4.

Jaki jest najlepszy sposób na przywrócenie właściwości do pierwotnie zakodowanych wartości domyślnych?

Odpowiedz

9

Możesz inicjalizować w metodzie zamiast wpisywać deklarację. Następnie wywołaj metodę konstruktora i zresetuj metodę inicjalizacji:

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

    public Truck() { 
     Init(); 
    } 

    public void ResetTruck() { 
     Init(); 
    } 

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

Innym sposobem nie jest w ogóle metoda resetowania. Po prostu utwórz nową instancję.

+1

W pewnym stopniu tak właśnie robiłem, ale zastanawiałem się, czy istnieje lepszy sposób. –

0

Prawdopodobnie trzeba będzie zapisać wartości w polach prywatnych, aby można było je później przywrócić. Może coś takiego:

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

Jeśli miała to podejście, wtedy bym deklarować wartości domyślne jako const statycznej. W końcu używasz ich jak stałych. – AaronLS

+0

To prawda, zmienię to –

2

Jeśli tak swoją inicjację w sposobie reset może być dobry, aby przejść:

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

    public Truck() { 
    ResetTruck(); 
    } 

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

ile tworzenia obiektu jest bardzo drogie (i Reset nie jest z jakiegoś powodu). Nie widzę powodu, aby zaimplementować specjalną metodę resetowania. Dlaczego po prostu nie stworzysz nowej instancji z użytecznym stanem domyślnym.

Jaki jest cel ponownego użycia instancji?

+0

Ponieważ sam obiekt jest tym, co będzie robić resetowanie. To nie zależy od kodu, który zainicjuje obiekt w celu zainicjowania resetowania. –

+4

@Dylan: Wydaje mi się, że klasa może robić za dużo. Zapoznaj się z zasadą odpowiedzialności pojedynczej, aby uzyskać więcej informacji. –

0

Ty zasadniczo poszukuje Wzorca State projekt

+0

Przepraszam, ale NIE! Nie ma potrzeby narzucania schematu stanu, gdy istnieją czystsze rozwiązania, chociaż można go użyć, jeśli czujesz się jak masochista. :) – arviman

+0

Czy opracowania ... –

+0

Co mam do czynienia jest to, że można * można * użyć wzorca stanu tutaj, aby osiągnąć tę funkcjonalność, ale to nie ma znaczenia. Poleciłbym przeczytanie tego schematu, ponieważ wygląda na to, że jest on pomieszany z "automatami państwowymi". Wzorzec stanu pozwala zmienić zachowanie atrybutu kontekstu na podstawie stanu. Tutaj wszystko, co robisz, powraca do stanu początkowego. Nie trzeba zmieniać zachowań. – arviman

3

Odbicie jest twoim przyjacielem. Można utworzyć metodę pomocniczą, aby użyć Activator.CreateInstance() do ustawienia domyślnej wartości typów wartości i "null" dla typów referencyjnych, ale dlaczego zawracać sobie głowę przy ustawianiu wartości null na parametrze SetValue PropertyInfo, zrobi to samo.

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. 

Aby przedłużyć ten do celu, mają prywatne członków:

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

ustawić wartości w konstruktorze:

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

Wyczyść je później:

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

Wolę używać 'FormatterServices.GetUninitializedObject()' over 'Activator.CreateInstance()'. Konstruktory są pomijane, wszystkie pola mają domyślnie wartość null/default. –

2

Koncentracja separacji c oncerns (jak Brian wspomniano w komentarzach), inną alternatywą byłoby dodać typ TruckProperties (można nawet dodać wartości domyślne do jej konstruktora):

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

Wewnątrz klasy Truck, wszystko zrobisz jest zarządzanie instancji typu TruckProperties, i pozwól mu wykonać reset.

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

To z pewnością może być dużo (niechcianej) narzutu dla takiego prostego klasy, ale w większym/bardziej złożonego projektu może okazać się korzystne.

To jest rzecz o "najlepszych" praktykach ... wiele razy, nie ma srebrnej kuli, ale tylko zalecenia, które musisz przyjąć ze sceptycyzmem i najlepszą oceną, co odnosi się do ciebie w konkretnym przypadku.

0

Jeśli chcesz mieć określony "stan" swojego obiektu w przeszłości, możesz utworzyć określony punkt zapisu, który powróci za każdym razem. To również pozwala na uzyskanie innego stanu do tworzenia kopii zapasowych dla tworzonej instancji Everey. Jeśli twoja klasa ma wiele właściwości, które są w ciągłej zmianie, może to być twoje rozwiązanie.

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

Rozwiązałem podobny problem z refleksją. Możesz użyć source.GetType().GetProperties(), aby uzyskać listę wszystkich właściwości należących do obiektu.

Chociaż nie zawsze jest to kompletne rozwiązanie. Jeśli twój obiekt implementuje kilka interfejsów, otrzymasz te wszystkie właściwości za pomocą połączenia refleksyjnego.

Tak więc napisałem tę prostą funkcję, która daje nam większą kontrolę nad właściwościami, które są zainteresowane resetowaniem.

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."); 
      } 
     } 
    } 

I to zastosowanie:

// 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)); 
Powiązane problemy