2009-04-02 14 views
5

Tôi đã tạo một lớp với các thuộc tính có giá trị mặc định. Tại một số thời điểm trong vòng đời của đối tượng, tôi muốn "thiết lập lại" các thuộc tính của đối tượng trở lại với những gì chúng là khi đối tượng được khởi tạo. Ví dụ, giả sử đây là lớp:Làm cách nào để khởi động lại hoặc đặt lại các thuộc tính của một lớp?

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

    public Truck() { } 

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

Sau đó, tại một số điểm, sau khi NameTires thuộc tính đã được thay đổi, phương pháp ResetTruck() có thể được gọi và các thuộc tính sẽ được thiết lập lại trở lại với "Super Xe tải" và 4, tương ứng.

Cách tốt nhất để đặt lại thuộc tính về giá trị mặc định được mã hóa ban đầu của chúng là gì?

Trả lời

9

Bạn có thể khởi tạo phương thức thay vì nội tuyến với khai báo. Sau đó, có phương thức khởi tạo và đặt lại, hãy gọi phương thức khởi tạo:

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

    public Truck() { 
     Init(); 
    } 

    public void ResetTruck() { 
     Init(); 
    } 

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

Một cách khác là không có phương pháp đặt lại. Chỉ cần tạo một phiên bản mới.

+1

Ở một mức độ nào đó, đây thực sự là những gì tôi đang làm, nhưng tôi đã tự hỏi liệu có cách nào tốt hơn không. –

0

Có thể bạn cần phải lưu các giá trị trong các trường riêng tư để chúng có thể được khôi phục sau này. Có thể một cái gì đó như thế này:

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

Nếu bạn mất phương pháp này, sau đó tôi muốn tuyên bố các giá trị mặc định như const tĩnh. Sau khi tất cả các bạn đang sử dụng chúng như hằng số. – AaronLS

+0

Đúng, tôi sẽ thay đổi –

2

Nếu bạn đã khởi tạo của bạn trong một phương pháp lập lại bạn có thể được tốt để đi:

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

    public Truck() { 
    ResetTruck(); 
    } 

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

Trừ khi tạo các đối tượng thực sự đắt (và Reset không phải là vì một lý do). Tôi thấy không có lý do để thực hiện một phương pháp thiết lập lại đặc biệt. Tại sao bạn không chỉ tạo một cá thể mới với trạng thái mặc định có thể sử dụng được.

Mục đích của việc sử dụng lại cá thể là gì?

+0

Bởi vì đối tượng chính nó là những gì sẽ được thực hiện việc đặt lại. Nó không phải là mã mà instantiates đối tượng để bắt đầu thiết lập lại. –

+4

@ Dylan: Nghe có vẻ như tôi có thể làm quá nhiều. Hãy xem Nguyên tắc về trách nhiệm duy nhất để biết thêm thông tin. –

0

Về cơ bản, bạn đang tìm kiếm State Mẫu thiết kế

+0

Xin lỗi nhưng KHÔNG! Điều này không cần chi phí của một mô hình nhà nước khi có các giải pháp sạch hơn, mặc dù bạn có thể sử dụng nó nếu bạn đang cảm thấy giống như một masochist. :) – arviman

+0

Làm phức tạp ... –

+0

Điều tôi nhận được là bạn * có thể * sử dụng mẫu trạng thái ở đây để đạt được chức năng này, nhưng nó không liên quan. Tôi sẽ khuyên bạn nên đọc qua các mô hình kể từ khi nó xuất hiện bạn đã có nó messed với "máy nhà nước". Mẫu trạng thái cho phép bạn thay đổi hành vi của một thuộc tính của ngữ cảnh dựa trên trạng thái. Ở đây, tất cả những gì bạn đang làm là hoàn nguyên về trạng thái ban đầu. Không cần thay đổi hành vi cần thiết. – arviman

3

Phản ánh là bạn của bạn. Bạn có thể tạo một phương thức trợ giúp để sử dụng Activator.CreateInstance() để thiết lập giá trị mặc định của các kiểu giá trị và 'null' cho các kiểu tham chiếu, nhưng tại sao bận tâm khi thiết lập null trên SetValue của PropertyInfo sẽ làm như vậy.

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. 

Để mở rộng này cho mục đích của bạn, có các thành viên riêng:

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

Đặt các giá trị trong constructor của bạn:

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

Đặt lại chúng sau:

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

Tôi thích sử dụng 'FormatterServices.GetUninitializedObject()' trên 'Activator.CreateInstance()'. Constructors bị bỏ qua, tất cả các trường được bảo đảm là null/default. –

2

Tập trung phân tách c oncerns (như Brian đề cập trong các ý kiến), thay thế khác sẽ có thêm một loại TruckProperties (thậm chí bạn có thể thêm các giá trị mặc định của bạn để constructor của nó):

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

Bên trong lớp Truck của bạn, tất cả các bạn sẽ làm là quản lý một thể hiện của loại TruckProperties và để thiết lập lại.

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

Điều này chắc chắn có nhiều chi phí không mong muốn cho một lớp đơn giản như vậy, nhưng trong một dự án lớn hơn/phức tạp hơn, nó có thể thuận lợi.

Đó là điều về thực hành "tốt nhất" ... nhiều lần, không có viên đạn bạc, nhưng chỉ những khuyến nghị bạn phải thực hiện với sự hoài nghi và phán đoán tốt nhất của bạn về những gì áp dụng cho bạn trong trường hợp cụ thể.

0

Nếu bạn muốn một "trạng thái" cụ thể trong quá khứ của đối tượng, bạn có thể tạo một điểm lưu cụ thể để trả về mỗi lần bạn muốn. Điều này cũng cho phép bạn có một trạng thái khác nhau để sao lưu cho trường hợp everey mà bạn tạo ra. Nếu lớp của bạn có nhiều thuộc tính thay đổi liên tục, thì đây có thể là giải pháp của bạ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

Tôi đã giải quyết được vấn đề tương tự với sự phản ánh. Bạn có thể sử dụng source.GetType().GetProperties() để có danh sách tất cả thuộc tính thuộc về đối tượng.

Mặc dù, đây không phải lúc nào cũng là giải pháp hoàn chỉnh. Nếu đối tượng của bạn thực hiện một số giao diện, bạn cũng sẽ nhận được tất cả các thuộc tính đó với lời gọi phản chiếu của bạn.

Vì vậy, tôi đã viết chức năng đơn giản này cho phép chúng tôi kiểm soát nhiều hơn các thuộc tính mà chúng tôi quan tâm đến việc đặt lại.

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

Và nó sử dụng:

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