12

我创建了一个具有默认值属性的类。在对象生命周期的某个时刻,我想将对象的属性“重置”回实例化对象时的状态。例如,假设这是一个类:

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

   public Truck() { }

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

然后在某个时刻,在NameTires属性被更改后,ResetTruck()可以调用该方法,并将属性分别重置回“超级卡车”和 4。

将属性重置为其初始硬编码默认值的最佳方法是什么?

4

10 回答 10

22

您可以在方法中进行初始化,而不是在声明中内联。然后让构造函数和重置方法调用初始化方法:

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

   public Truck() {
      Init();
   }

   public void ResetTruck() {
      Init();
   }

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

另一种方法是根本没有重置方法。只需创建一个新实例。

于 2009-04-02T05:03:13.060 回答
10

反射是你的朋友。您可以创建一个辅助方法来使用 Activator.CreateInstance() 来设置值类型的默认值,并为引用类型设置“null”,但是为什么在 PropertyInfo 的 SetValue 上设置 null 时会这样做。

    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);     
    }
}
于 2015-08-14T19:53:00.740 回答
5

除非创建对象真的很昂贵(并且重置不是出于某种原因)。我认为没有理由实施特殊的重置方法。为什么不直接创建一个具有可用默认状态的新实例。

重用实例的目的是什么?

于 2009-04-02T05:24:21.747 回答
3

如果您在 Reset 方法中进行了初始化,则可以:

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

   public Truck() {
    ResetTruck();
  }

   public void ResetTruck() {
      Name = "Super Truck";
      Tires = 4;
   }
}
于 2009-04-02T05:03:59.953 回答
2

关注关注点分离(如评论中提到的 Brian),另一种选择是添加一个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();
    }
}

对于这样一个简单的类,这当然可能是很多(不需要的)开销,但在更大/更复杂的项目中,它可能是有利的。

这就是关于“最佳”实践的事情......很多时候,没有灵丹妙药,但只有建议您必须持怀疑态度和您对在特定情况下适用于您的最佳判断。

于 2015-08-14T20:25:57.013 回答
1

我用反射解决了类似的问题。您可以使用 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));
于 2017-05-10T21:58:46.253 回答
0

您可能需要将值保存在私有字段中,以便以后可以恢复它们。也许是这样的:

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

}
于 2009-04-02T05:01:49.740 回答
0

您本质上是在寻找状态设计模式

于 2009-04-02T07:11:39.397 回答
0

如果您想要对象的特定过去“状态”,您可以创建一个特定的保存点以在每次需要时返回。这也让您可以为您创建的每个实例备份不同的状态。如果您的班级有许多不断变化的属性,这可能是您的解决方案。

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));
    }
}
于 2016-08-17T14:59:24.240 回答
0

如果您没有使用会发生冲突的代码生成器或设计器,另一种选择是通过 C# 的TypeDescriptor东西,这类似于反射,但意味着向类添加比反射更多的元信息。

using System.ComponentModel;

public class Truck {
   // You can use the DefaultValue Attribute for simple primitive properites
   [DefaultValue("Super Truck")]
   public string Name { get; set; } = "Super Truck";

   // You can use a Reset[PropertyName]() method for more complex properties
   public int Tires { get; set; } = 4;
   public void ResetTires() => Tires = 4; 
   

   public Truck() { }

   public void ResetTruck() {
      // Iterates through each property and tries to reset it
      foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(GetType())) {
        if (prop.CanResetValue(this))  prop.ResetValue(this);
      }
   }
}

请注意,如果存在,ResetValue 也将重置为阴影属性。文档中解释了选择哪个选项的优先级:

此方法按以下优先顺序确定要将属性重置为的值:

  1. 此属性有一个阴影属性。
  2. 此属性有一个 DefaultValueAttribute。
  3. 您已经实现了一个“ResetMyProperty”方法,其中“MyProperty”是您传递给它的属性的名称。
于 2021-07-27T01:52:50.940 回答