2009-04-02 5 views
5

J'ai créé une classe avec des propriétés ayant des valeurs par défaut. À un certain moment dans la vie de l'objet, je voudrais «réinitialiser» les propriétés de l'objet à ce qu'elles étaient lorsque l'objet a été instancié. Par exemple, disons que ce fut la classe:Comment réinitialiser ou réinitialiser les propriétés d'une 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 
    } 
} 

Puis à un moment donné, après les propriétés Name et Tires ont été modifiés, la méthode ResetTruck() pourrait être appelé et les propriétés seraient réinitialisé à « Super Truck » et 4, respectivement.

Quelle est la meilleure façon de réinitialiser les propriétés à leurs valeurs par défaut initiales codées en dur?

Répondre

9

Vous pouvez avoir l'initialisation dans une méthode au lieu de l'inlining avec la déclaration. Alors le constructeur et réinitialiser méthode appeler la méthode d'initialisation:

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

    public Truck() { 
     Init(); 
    } 

    public void ResetTruck() { 
     Init(); 
    } 

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

Une autre façon est de ne pas avoir une méthode de remise à zéro du tout. Créez simplement une nouvelle instance.

+1

Dans une certaine mesure, c'est en fait ce que je faisais, mais je me demandais s'il y avait une meilleure façon. –

0

Vous devrez probablement enregistrer les valeurs dans des champs privés pour pouvoir les restaurer ultérieurement. Peut-être quelque chose comme ceci:

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 vous avez pris cette approche, alors je déclare les valeurs par défaut const statique. Après tout, vous les utilisez comme des constantes. – AaronLS

+0

Vraiment, je vais changer cela –

2

Si vous avez fait votre initialisation dans une méthode de réinitialisation, vous pouvez être bon d'aller:

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

    public Truck() { 
    ResetTruck(); 
    } 

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

À moins la création de l'objet est vraiment cher (et Reset est pas pour une raison quelconque). Je ne vois aucune raison d'implémenter une méthode de réinitialisation spéciale. Pourquoi ne créez-vous pas simplement une nouvelle instance avec un état par défaut utilisable.

Quel est le but de la réutilisation de l'instance?

+0

Parce que l'objet lui-même est ce qui va faire la réinitialisation. Ce n'est pas le code qui instancie l'objet pour initier la réinitialisation. –

+4

@Dylan: J'ai l'impression que la classe en fait peut-être trop. Jetez un coup d'œil au principe de responsabilité unique pour plus d'informations. –

0

Vous êtes à la recherche essentiellement pour le modèle de conception State

+0

Désolé mais NON! Cela n'a pas besoin de la surcharge d'un modèle d'état quand il y a des solutions plus propres, bien que vous puissiez l'utiliser si vous vous sentez comme un masochiste. :) – arviman

+0

Ne pas élaborer ... –

+0

Ce que je veux dire, c'est que vous * pouvez * utiliser le modèle d'état ici pour réaliser cette fonctionnalité, mais ce n'est pas pertinent. Je vous recommande de lire le modèle car il semble que vous l'ayez raté avec "Machines d'état". Le modèle d'état vous permet de modifier le comportement d'un attribut d'un contexte en fonction de l'état. Ici, tout ce que vous faites revient à un état initial. Il n'y a pas de changement de comportement nécessaire. – arviman

3

La réflexion est votre ami. Vous pouvez créer une méthode d'assistance pour utiliser Activator.CreateInstance() pour définir la valeur par défaut de Types de valeur et 'null' pour les types de référence, mais pourquoi s'inquiéter lors de la définition de null sur SetValue d'une PropertyInfo fera la même chose.

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. 

Pour étendre ce à vos besoins, avoir des membres privés:

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

Définissez les valeurs dans votre constructeur:

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

Réinitialiser les plus tard:

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

Je préfère utiliser 'FormatterServices.GetUninitializedObject()' over 'Activator.CreateInstance()'. Les constructeurs sont ignorés, tous les champs sont garantis null/default. –

2

Mise au point de la séparation de c es préoccupations (comme Brian mentionné dans les commentaires), une autre alternative serait d'ajouter un type TruckProperties (vous pouvez même ajouter vos valeurs par défaut à son constructeur):

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

l'intérieur de votre Truck classe, tout ce que vous feriez est de gérer une instance du type TruckProperties et laissez-le faire sa réinitialisation.

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

Cela peut certainement beaucoup de frais généraux (non désiré) pour une telle classe simple, mais dans un plus grand/projet plus complexe, il pourrait être avantageux. C'est ce que l'on appelle les «meilleures» pratiques ... souvent, il n'y a pas de solution miracle, mais seulement des recommandations que vous devez prendre avec scepticisme et votre meilleur jugement quant à ce qui vous concerne dans un cas particulier.

0

Si vous voulez un "état" passé spécifique de votre objet, vous pouvez créer un point de sauvegarde particulier à renvoyer chaque fois que vous le souhaitez. Cela vous permet également d'avoir un état différent à sauvegarder pour chaque instance que vous créez. Si votre classe a de nombreuses propriétés en constante évolution, cela pourrait être votre solution.

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

J'ai résolu un problème similaire avec la réflexion. Vous pouvez utiliser source.GetType().GetProperties() pour obtenir une liste de toutes les propriétés appartenant à l'objet.

Bien que ce ne soit pas toujours une solution complète. Si votre objet implémente plusieurs interfaces, vous obtiendrez également toutes ces propriétés avec votre appel de réflexion.

J'ai donc écrit cette fonction simple qui nous donne plus de contrôle sur les propriétés que nous sommes intéressés à réinitialiser.

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

Et son utilisation:

// 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)); 
Questions connexes