2009-04-02 19 views
5

He creado una clase con propiedades que tienen valores predeterminados. En algún punto de la vida útil del objeto, me gustaría "restablecer" las propiedades del objeto de nuevo a lo que eran cuando se creó una instancia del objeto. Por ejemplo, digamos que esta era la clase:¿Cómo reinicio o reinicio las propiedades de una clase?

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

    public Truck() { } 

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

A continuación, en algún momento, después de que los Name y Tires propiedades se han cambiado, el método ResetTruck() podría ser llamado y las propiedades se restablece de nuevo a "Super Truck" y 4, respectivamente.

¿Cuál es la mejor manera de restablecer las propiedades a sus valores predeterminados predeterminados?

Respuesta

9

Puede tener la inicialización en un método en lugar de alinear con la declaración. Luego tener el constructor y restablecer método de llamar al método de inicialización:

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

    public Truck() { 
     Init(); 
    } 

    public void ResetTruck() { 
     Init(); 
    } 

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

Otra forma es no tener un método de restablecimiento en absoluto. Solo crea una nueva instancia.

+1

Hasta cierto punto, esto es realmente lo que estaba haciendo, pero me preguntaba si había una mejor manera. –

0

Probablemente necesite guardar los valores en campos privados, para que puedan ser restaurados más tarde. Tal vez algo como esto:

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

Si tomó este enfoque, entonces me declaro los valores por defecto como static const. Después de todo, los estás usando como constantes. – AaronLS

+0

Es cierto, voy a cambiar eso –

2

Si usted hizo su inicialización en un método de reinicio puede ser bueno para ir:

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

    public Truck() { 
    ResetTruck(); 
    } 

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

A menos que crear el objeto es muy caro (y Reset no es por alguna razón). No veo ninguna razón para implementar un método de reinicio especial. ¿Por qué no creas una nueva instancia con un estado predeterminado utilizable?

¿Cuál es el propósito de reutilizar la instancia?

+0

Porque el objeto en sí es lo que hará el restablecimiento. No corresponde al código que ejemplifique el objeto para iniciar el restablecimiento. –

+4

@Dylan: Me parece que la clase puede estar haciendo demasiado entonces. Eche un vistazo al Principio de Responsabilidad Individual para más información. –

0

básicamente estás buscando el patrón State Diseño

+0

Lo siento pero NO! Esto no necesita la sobrecarga de un patrón de estado cuando hay soluciones más limpias, aunque podrías usarlo si te sientes como un masoquista. :) – arviman

+0

Elabore ... –

+0

Lo que quiero decir es que * podría * usar el patrón de estado aquí para lograr esta funcionalidad, pero no es relevante. Le recomendaría que lea todo el patrón, ya que parece que lo ha confundido con "Máquinas de estado". El patrón de estado le permite cambiar el comportamiento de un atributo de un contexto basado en el estado. Aquí, todo lo que estás haciendo es volver a un estado inicial. No hay cambio en el comportamiento necesario. – arviman

3

La reflexión es su amigo. Puede crear un método auxiliar para usar Activator.CreateInstance() para establecer el valor predeterminado de los tipos de valor y 'null' para los tipos de referencia, pero por qué molestarse al establecer null en un SetValue PropertyInfo hará lo mismo.

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. 

Para extender esto para su propósito, tienen miembros privados:

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

establecer los valores en su constructor:

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

restablecerlos más tarde:

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

Prefiero usar 'FormatterServices.GetUninitializedObject()' sobre 'Activator.CreateInstance()'. Los constructores se omiten, se garantiza que todos los campos son nulos/predeterminados. –

2

Enfoque de separación de c oncerns (como Brian mencionó en los comentarios), otra alternativa sería añadir un tipo TruckProperties (incluso se podría añadir sus valores por defecto a su constructor):

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 de su clase Truck, todo lo que se hacer es gestionar una instancia del tipo TruckProperties y deje que se restablezca.

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

Esto sin duda puede ser un montón de gastos generales (no deseada) para una clase tan simple, pero en un proyecto más grande/más complejo que podría ser ventajosa.

Eso es lo de las "mejores" prácticas ... muchas veces, no hay una solución mágica, pero solo recomendaciones que debe tomar con escepticismo y su mejor juicio en cuanto a lo que se aplica a usted en un caso particular.

0

Si desea un "estado" pasado específico de su objeto, puede crear un punto de guardado específico para devolver cada vez que desee. Esto también le permite tener un estado diferente de copia de seguridad para cada instancia que crea. Si su clase tiene muchas propiedades que están en constante cambio, esta podría ser su solución.

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

Resolví un problema similar con la reflexión. Puede usar source.GetType().GetProperties() para obtener una lista de todas las propiedades que pertenecen al objeto.

Aunque esto no siempre es una solución completa. Si su objeto implementa varias interfaces, también obtendrá todas esas propiedades con su llamada de reflexión.

Así que escribí esta sencilla función que nos da un mayor control de las propiedades que nos interesa restablecer.

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

Y es su 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)); 
Cuestiones relacionadas