Как мне повторно инициализировать или сбросить свойства класса? - PullRequest
7 голосов
/ 02 апреля 2009

Я создал класс со свойствами, которые имеют значения по умолчанию. В какой-то момент в жизни объекта я хотел бы «сбросить» свойства объекта обратно к тому, что было, когда объект был создан. Например, скажем, это был класс:

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

   public Truck() { }

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

Затем, в какой-то момент, после изменения свойств Name и Tires, можно вызвать метод ResetTruck() и свойства будут сброшены обратно до «Super Truck» и 4 соответственно.

Каков наилучший способ восстановить свойства до их начальных жестко заданных значений по умолчанию?

Ответы [ 9 ]

11 голосов
/ 02 апреля 2009

Вы можете иметь инициализацию в методе вместо встраивания с объявлением. Затем пусть конструктор и метод reset вызывают метод инициализации:

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

   public Truck() {
      Init();
   }

   public void ResetTruck() {
      Init();
   }

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

Другой способ - вообще не использовать метод сброса. Просто создайте новый экземпляр.

4 голосов
/ 02 апреля 2009

Если создание объекта не является действительно дорогим (и Reset не по какой-то причине). Я не вижу причин для реализации специального метода сброса. Почему бы вам просто не создать новый экземпляр с пригодным для использования состоянием по умолчанию.

Какова цель повторного использования экземпляра?

3 голосов
/ 14 августа 2015

Отражение - твой друг. Можно создать вспомогательный метод для использования Activator.CreateInstance (), чтобы установить значение по умолчанию для типов значений и значение «null» для ссылочных типов, но зачем беспокоиться, если установка значения NULL в PropertyInfo в SetValue будет делать то же самое.

    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.

Чтобы расширить это для вашей цели, есть частные члены:

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

Установите значения в вашем конструкторе:

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

Сброс их позже:

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);     
    }
}
3 голосов
/ 02 апреля 2009

Если вы выполнили инициализацию с помощью метода Reset, вам может пригодиться:

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

   public Truck() {
    ResetTruck();
  }

   public void ResetTruck() {
      Name = "Super Truck";
      Tires = 4;
   }
}
2 голосов
/ 14 августа 2015

Сосредоточение внимания на разделении интересов (как упомянул Брайан в комментариях), другой альтернативой будет добавление типа TruckProperties (вы даже можете добавить значения по умолчанию в его конструктор):

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

Внутри вашего класса Truck все, что вам нужно сделать, это управлять экземпляром типа TruckProperties и позволить ему выполнить его сброс.

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

Это, конечно, может быть много (нежелательных) накладных расходов для такого простого класса, но в большем / более сложном проекте это может быть выгодно.

Это то, что относится к «лучшим» практикам ... много раз, серебряной пули нет, а есть только рекомендации, которые вы должны принимать со скептицизмом и ваше лучшее суждение относительно того, что относится к вам в конкретном случае.

0 голосов
/ 11 мая 2017

Я решил похожую проблему с отражением. Вы можете использовать source.GetType().GetProperties(), чтобы получить список всех свойств, которые принадлежат объекту.

Хотя это не всегда полное решение. Если ваш объект реализует несколько интерфейсов, вы также получите все эти свойства с помощью вызова рефлексии.

Итак, я написал эту простую функцию, которая дает нам больше контроля над тем, какие свойства мы заинтересованы в сбросе.

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

И это использовать:

// 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));
0 голосов
/ 17 августа 2016

Если вы хотите определенное прошлое «состояние» вашего объекта, вы можете создать определенную точку сохранения, которая будет возвращаться каждый раз, когда вы захотите. Это также позволяет создавать резервные копии для каждого экземпляра, который вы создаете. Если в вашем классе много свойств, которые постоянно изменяются, это может быть вашим решением.

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 голосов
/ 02 апреля 2009

Вы, по сути, ищете State Design Pattern

0 голосов
/ 02 апреля 2009

Возможно, вам потребуется сохранить значения в личных полях, чтобы их можно было восстановить позже. Может быть, что-то вроде этого:

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

}
...