I am trying to implement a string indexer for a C# class, but when you set a property the dictionary gets set and not the property. It is probably something simple that i am missing, i just can't see it.
objFiveProp temp = new objFiveProp();
temp["index1"] = 3;
sets the temp._items["index1"].value to 3.
Class:
public class objFiveProp
{
#region Properties
private Dictionary<string, int> _items;
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; }
}
public int index1 { get; set; }
public int index2 { get; set; }
public int index3 { get; set; }
public int index4 { get; set; }
public int index5 { get; set; }
#endregion
#region Constructor
public objFiveProp()
{
index1 = 0;
index2 = 0;
index3 = 0;
index4 = 0;
index5 = 0;
_items = new Dictionary<string, int>();
_items.Add("index1", index1);
_items.Add("index2", index2);
_items.Add("index3", index3);
_items.Add("index4", index4);
_items.Add("index5", index5);
}
#endregion
}
That's how it works. The Dictionary contains a copy of the integers you use to set it up - not a reference to the properties.
I would tackle this by using something like:
public class objFiveProp
{
private Dictionary<string, int> _items;
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; }
}
public int Index1
{
get { return this["index1"]; }
set { this["index1"] = value; }
}
public int Index2
{
get { return this["index2"]; }
set { this["index2"] = value; }
}
// ....
public objFiveProp()
{
_items = new Dictionary<string, int>();
_items.Add("index1", index1);
_items.Add("index2", index2);
_items.Add("index3", index3);
_items.Add("index4", index4);
_items.Add("index5", index5);
}
#endregion
This causes your properties to always pull the values stored in your dictionary, as well as save there, so there aren't two copies of the values.
This is because in index set method you are setting the value of dictionary item
public int this[string key]
{
get { return _items[key]; }
set { _items[key] = value; } //here you are setting the value of dictionary item not the property
}
Either, create separate property for index1, index2 and so on or in set method above add checks, a dirty solution though, to set the value of member variable depending on the value of key; Something like:
set {
_items[key] = value;
if(key == "index1")
index1 = value;
}
int is a value type, not a reference type. When you set a value in the _items, it won't set the property, even though you initially added it from the property.
From MSDN
Variables that are based on value types directly contain values.
Assigning one value type variable to another copies the contained
value. This differs from the assignment of reference type variables,
which copies a reference to the object but not the object itself.
If you really need to be able to access your data both from the indexer and the property, one of the simplest way would be to rewrite your properties thusly:
public int indexN
{
get { return _items["indexN"]; }
set { _items["indexN"] = value; }
}
Another way would be to use reflection in the indexer's setter:
public int this[string key]
{
get { return _items[key]; }
set
{
_items[key] = value;
PropertyInfo prop = this.GetType().GetProperty(key);
if (prop != null)
{
prop.SetValue(this, null);
}
}
}
Remember though, reflection is relatively very S-L-O-W.
There are other ways to accomplish what you're trying to do as well, but perhaps the best solution is to not do this at all. Pick the best interface to your class, whether that be the indexer or the properties, and stick with it. Your code will be more maintainable (you won't have to upkeep two public interfaces to your class's data) and more readable (other coders won't need to know that the indexer and the properties are the same thing). Cheers!
Related
public class TestClass
{
private Dictionary<string, int> _testDictionary = new Dictionary<string,int>();
private string _key;
public int this[string key]
{
get { return _testDictionary[key];}
set
{
if (_testDictionary.ContainsKey(key))
_testDictionary[key] = value;
else
_testDictionary.Add(key, value);
}
}
}
public class Program
{
static void Main(string[] args)
{
TestClass test = new TestClass();
test["T1"] = 1;
test["T2"] = 2;
Console.WriteLine(test["T1"]);
Console.WriteLine(test["T2"]);
}
}
So how this way of defining properties is called, I want to read more about that. Also is it possible to have same definition like this in other places, for an example in Methods etc.
Your implementation is right, you could add your desired IndexerName but you don't have to. It is better to add a guide from the getter in case the key is not found, and you return some default value.
Check out this enter link description here
public class TestClass
{
private Dictionary<string, int> _testDictionary = new Dictionary<string, int>();
// you do not need a private property to store the key
// private string _key;
[IndexerName("MyKeyItem")]
public int this[string key]
{
get
{
if (_testDictionary.ContainsKey(key))
{
return _testDictionary[key];
}
return int.MinValue;
}
set
{
if (_testDictionary.ContainsKey(key))
_testDictionary[key] = value;
else
_testDictionary.Add(key, value);
}
}
}
It's called an indexed property.
I have a base class Rules.cs. There are 2 derived classes RowRules.cs and ColumnRules.cs. I have another class Test.cs. This class has a Dictionary <int, Rules> which keeps adding the values. When I loop through the dictionary I need to know if the value is a RowRule or a ColumnRule. To better understand I have the code below.
Rules.cs
class Rules
{
private int m_timepointId = 0;
private int m_studyId = 0;
public int TimepointId
{
get { return m_timepointId; }
set { m_timepointId = value;}
}
public int StudyId
{
get { return m_studyId; }
set {m_studyId = value; }
}
}
RowRules.cs
class RowRules : Rules
{
private int m_row;
public int Row
{
get { return m_row; }
set { m_row = value; }
}
}
ColumnRules.cs
class ColumnRules: Rules
{
private int m_column;
public int Column
{
get { return m_column; }
set { m_column = value; }
}
}
In the main class I have
private Dictionary<int, Rules> m_testDictionary = new Dictionary<int, Rules>();
ColumnRules columnrules = new ColumnRules();
RowRules rowRules = new RowRules();
rowRules.Row = 1;
rowRules.StudyId = 1;
m_testDictionary.Add(1, rowRules);
columnRules.Column = 2;
columnRules.TimepointId = 2;
m_testDictionary.Add(2, columnRules);
foreach(.... in m_testDictionary)
{
//Need code here.
//if(... == RowRules)
{
}
}
Now, I need to know what value will go in the foreach loop. Also, I need to know whether that particular dictionary row is a RowRule or a ColumnRule. Hope I am clear with the question. Any help will be really appreciated.
There are a bunch of answers that are telling you to test the type using "is". That's fine, but in my opinion if you're switching off the type of an object, you're probably doing something wrong.
Typically, derived classes are used when you need additional and varied functionality from a base class. Moreover, ad-hoc polymorphism via virtual and abstract methods means that you can let the run-time figure out the type, leading to significantly cleaner code.
For example, in your case, you might want to make Rules an abstract class, with an abstract ApplyRule() method. Then, each subclass can implement the method, with the full knowledge of what it means to be a rule of that type:
public class Rules
{
private int m_timepointId = 0;
private int m_studyId = 0;
public int TimepointId
{
get { return m_timepointId; }
set { m_timepointId = value;}
}
public int StudyId
{
get { return m_studyId; }
set {m_studyId = value; }
}
// New method
public abstract void ApplyRule();
}
class RowRules : Rules
{
private int m_row;
public int Row
{
get { return m_row; }
set { m_row = value; }
}
public override void ApplyRule() { // Row specific implementation }
}
class ColumnRules : Rules
{
private int m_column;
public int Column
{
get { return m_column; }
set { m_column = value; }
}
public override void ApplyRule() { // Column specific implementation }
}
Now, your loop is just:
foreach(var kvp in m_testDictionary)
{
kvp.Value.ApplyRule();
}
This should work:
foreach(KeyValuePair<int, Rules> pair in m_testDictionary)
{
if(pair.Value is RowRule)
{
// do row rule stuff
}
if(pair.Value is ColumnRule)
{
// do row column rule stuff
}
}
Here is more information on the is keyword.
Try the following
foreach(var rule in in m_testDictionary.Values)
{
var rowRules = rule as RowRules;
if (rowRules != null) {
// It's a RowRules
continue;
}
var columnRules = rule as ColumnRules;
if (columnRules != null) {
// It's a ColumnRules
continue;
}
}
You can try this:
foreach(var key in m_testDictionary.Keys)
{
var value = m_testDictionary[key];
if(value is RowRules)
{
//test your code.....
}
}
does that code work? You have added the same key twice I believe. This is the code you wanted I believe:
foreach(int key in m_testDictionary.Keys)
{
RowRules row = m_testDictionary[key] as RowRules;
if(row !=null)
{
//code here:)
}
}
If my understanding of the internal workings of this line is correct:
public int MyInt { get; set; }
Then it behind the scenes does this:
private int _MyInt { get; set; }
Public int MyInt {
get{return _MyInt;}
set{_MyInt = value;}
}
What I really need is:
private bool IsDirty { get; set; }
private int _MyInt { get; set; }
Public int MyInt {
get{return _MyInt;}
set{_MyInt = value; IsDirty = true;}
}
But I would like to write it something like:
private bool IsDirty { get; set; }
public int MyInt { get; set{this = value; IsDirty = true;} }
Which does not work. The thing is some of the objects I need to do the IsDirty on have dozens of properties and I'm hoping there is a way to use the auto getter/setter but still set IsDirty when the field is modified.
Is this possible or do I just have to resign myself to tripling the amount of code in my classes?
You'll need to handle this yourself:
private bool IsDirty { get; set; }
private int _myInt; // Doesn't need to be a property
Public int MyInt {
get{return _myInt;}
set{_myInt = value; IsDirty = true;}
}
There is no syntax available which adds custom logic to a setter while still using the automatic property mechanism. You'll need to write this with your own backing field.
This is a common issue - for example, when implementing INotifyPropertyChanged.
Create an IsDirty decorator (design pattern) to wrap some your properties requiring the isDirty flag functionality.
public class IsDirtyDecorator<T>
{
public bool IsDirty { get; private set; }
private T _myValue;
public T Value
{
get { return _myValue; }
set { _myValue = value; IsDirty = true; }
}
}
public class MyClass
{
private IsDirtyDecorator<int> MyInt = new IsDirtyDecorator<int>();
private IsDirtyDecorator<string> MyString = new IsDirtyDecorator<string>();
public MyClass()
{
MyInt.Value = 123;
MyString.Value = "Hello";
Console.WriteLine(MyInt.Value);
Console.WriteLine(MyInt.IsDirty);
Console.WriteLine(MyString.Value);
Console.WriteLine(MyString.IsDirty);
}
}
You can make it simple or complex. It depends on how much work you want to invest. You can use aspect oriented programming to add the aspect via an IL weaver into the IL code with e.g. PostSharp.
Or you can create a simple class that does handle the state for your property. It is so simple that the former approach only pays off if you have really many properties to handle this way.
using System;
class Dirty<T>
{
T _Value;
bool _IsDirty;
public T Value
{
get { return _Value; }
set
{
_IsDirty = true;
_Value = value;
}
}
public bool IsDirty
{
get { return _IsDirty; }
}
public Dirty(T initValue)
{
_Value = initValue;
}
}
class Program
{
static Dirty<int> _Integer;
static int Integer
{
get { return _Integer.Value; }
set { _Integer.Value = value; }
}
static void Main(string[] args)
{
_Integer = new Dirty<int>(10);
Console.WriteLine("Dirty: {0}, value: {1}", _Integer.IsDirty, Integer);
Integer = 15;
Console.WriteLine("Dirty: {0}, value: {1}", _Integer.IsDirty, Integer);
}
}
Another possibility is to use a proxy class which is generated at runtime which does add the aspect for you. With .NET 4 there is a class that does handle this aspect already for you. It is called ExpandObject which does notify you via an event when a property changes. The nice things is that ExpandoObject allows you to define at runtime any amount of properties and you get notifications about every change of a property. Databinding with WPF is very easy with this type.
dynamic _DynInteger = new ExpandoObject();
_DynInteger.Integer = 10;
((INotifyPropertyChanged)_DynInteger).PropertyChanged += (o, e) =>
{
Console.WriteLine("Property {0} changed", e.PropertyName);
};
Console.WriteLine("value: {0}", _DynInteger.Integer );
_DynInteger.Integer = 20;
Console.WriteLine("value: {0}", _DynInteger.Integer);
Yours,
Alois Kraus
I'm going to add on to Simon Hughes' answer. I propose the same thing, but add a way to allow the decorator class to update a global IsDirty flag automatically. You may find it to be less complex to do it the old-fashioned way, but it depends on how many properties you're exposing and how many classes will require the same functionality.
public class IsDirtyDecorator<T>
{
private T _myValue;
private Action<bool> _changedAction;
public IsDirtyDecorator<T>(Action<bool> changedAction = null)
{
_changedAction = changedAction;
}
public bool IsDirty { get; private set; }
public T Value
{
get { return _myValue; }
set
{
_myValue = value;
IsDirty = true;
if(_changedAction != null)
_changedAction(IsDirty);
}
}
}
Now you can have your decorator class automatically update some other IsDirty property in another class:
class MyObject
{
private IsDirtyDecorator<int> _myInt = new IsDirtyDecorator<int>(onValueChanged);
private IsDirtyDecorator<int> _myOtherInt = new IsDirtyDecorator<int>(onValueChanged);
public bool IsDirty { get; private set; }
public int MyInt
{
get { return _myInt.Value; }
set { _myInt.Value = value; }
}
public int MyOtherInt
{
get { return _myOtherInt.Value; }
set { _myOtherInt.Value = value; }
}
private void onValueChanged(bool dirty)
{
IsDirty = true;
}
}
I have created a custom Property<T> class to do common operations like that. I haven't used it thoroughly yet though, but it could be used in this scenario.
Code can be found here: http://pastebin.com/RWTWNNCU
You could use it as follows:
readonly Property<int> _myInt = new Property<int>();
public int MyInt
{
get { return _myInt.GetValue(); }
set { _myInt.SetValue( value, SetterCallbackOption.OnNewValue, SetDirty ); }
}
private void SetDirty( int oldValue, int newValue )
{
IsDirty = true;
}
The Property class handles only calling the passed delegate when a new value is passed thanks to the SetterCallbackOption parameter. This is default so it can be dropped.
UPDATE:
This won't work apparently when you need to support multiple types (besides int), because the delegate won't match then. You could ofcourse always adjust the code to suit your needs.
I want to create a property in C# that sets or returns an individual member of an array. Currently, I have this:
private string[] myProperty;
public string MyProperty[int idx]
{
get
{
if (myProperty == null)
myProperty = new String[2];
return myProperty[idx];
}
set
{
myProperty[idx] = value;
}
}
However, I get the following compile error:
Bad array declarator: To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type.
How about this: write a class that does one thing and one thing only: provide random access to elements of some underlying indexed collection. Give this class a this indexer.
For properties that you want to provide random access to, simply return an instance of this indexer class.
Trivial implementation:
public class Indexer<T>
{
private IList<T> _source;
public Indexer(IList<T> source)
{
_source = source;
}
public T this[int index]
{
get { return _source[index]; }
set { _source[index] = value; }
}
}
public static class IndexHelper
{
public static Indexer<T> GetIndexer<T>(this IList<T> indexedCollection)
{
// could cache this result for a performance improvement,
// if appropriate
return new Indexer<T>(indexedCollection);
}
}
Refactoring into your code:
private string[] myProperty;
public Indexer<string> MyProperty
{
get
{
return myProperty.GetIndexer();
}
}
This will allow you to have as many indexed properties as you want, without needing to expose those properties with the IList<T> interface.
You must use this as the property name for indexers.
C# allows only one indexed property per class, so you are forced to use this.
You can use it this way:
private string[] myProp;
public string[] MyProp
{
get
{
if (myProp == null)
{
myProp = new String[2];
}
return myProp;
}
set
{
myProp = value;
}
}
And it's possible to acces myProp[1] as MyProp[1] for Example
Exposing your array through a read-only property might cover your needs. Since you don't want to allow other code to assign the array as such, there is no need for a public setter:
private string[] myProperty;
public string[] MyProperty
{
get
{
if (myProperty == null)
{
myProperty = new String[2];
}
return myProperty;
}
}
Then you can write code as such:
theObject.MyProperty[1] = "some string";
...but you cannot replace the array itself:
theObject.MyProperty = new string[2]; // will not compile
An option is to recode it as follows:
private string[] myProperty = new string[2];
public string[] MyProperty
{
get
{
return myProperty;
}
set
{
myProperty = value;
}
}
It'll compile, but it does have its own set of issues (fxCop will yell about it, but it can lead you to other options).
You could do something like this:
class Indexers
{
private string[] _strings = new [] {"A","B"};
private int[] _ints = new[] { 1, 2 };
public string[] Strings
{
get{ return _strings;}
}
public int[] Ints
{
get{ return _ints;}
}
}
class Program
{
static void Main(string[] args)
{
Indexers indexers = new Indexers();
int a1 = indexers.Ints[0];
string a2 = indexers.Strings[0];
}
}
C# provides no built-in mechanism to create indexed properties. You can use a class-level indexer (using this[int index] notation), but nothing like this on a property level.
One option is to create a helper class with an indexer and use this class as the property type. See an example on MSDN.
First, in-field declaration avoids excess check:
private string[] myProperty = new string[2];
You can implement several indexers via overloading by input type:
public string this[int index]
{
get
{
return myProperty[index];
}
set
{
myProperty[index] = value;
}
}
public object this[object a, object b] // different input type(s) (and different return type)
{
get
{
// do other stuff
}
}
You need to use an indexer. It works a little differently. See example:
public class Node
{
public Node this[int offset]
{
get { return localList[offset]; }
}
}
Note: You are allowed only one indexer per class. The reason is that it is too confusing to the compiler as to the meaning, so you only are allowed one.
You can also do this:
private static int[] _widget = new int[Counter];
public static int [] Widget
{
get { return _widget; }
set { _widget = value; }
}
...
for (int i = 0; i < MyClass.Counter; i++)
{
MyClass.Widget[i] = i;
}
...
double _newWidget5 = MyClass.Widget[5];
// and so on...
I've created a class with properties that have default values. At some point in the object's lifetime, I'd like to "reset" the object's properties back to what they were when the object was instantiated. For example, let's say this was the class:
public class Truck {
public string Name = "Super Truck";
public int Tires = 4;
public Truck() { }
public void ResetTruck() {
// Do something here to "reset" the object
}
}
Then at some point, after the Name and Tires properties have been changed, the ResetTruck() method could be called and the properties would be reset back to "Super Truck" and 4, respectively.
What's the best way to reset the properties back to their initial hard-coded defaults?
You can have the initialization in a method instead of inlining with the declaration. Then have the constructor and reset method call the initialization method:
public class Truck {
public string Name;
public int Tires;
public Truck() {
Init();
}
public void ResetTruck() {
Init();
}
private void Init() {
Name = "Super Truck";
Tires = 4;
}
}
Another way is not to have a reset method at all. Just create a new instance.
Reflection is your friend. You could create a helper method to use Activator.CreateInstance() to set the default value of Value types and 'null' for reference types, but why bother when setting null on a PropertyInfo's SetValue will do the same.
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.
To extend this for your purpose, have private members:
//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"};
Set the values in your 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));
}
}
Reset them later:
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);
}
}
Unless creating the object is really expensive (and Reset isn't for some reason). I see no reason to implement a special reset method. Why don't you just create a new instance with a usable default state.
What is the purpose of reusing the instance?
If you did your initialization in a Reset method you can be good to go:
public class Truck {
public string Name;
public int Tires;
public Truck() {
ResetTruck();
}
public void ResetTruck() {
Name = "Super Truck";
Tires = 4;
}
}
Focusing of separation of concerns (like Brian mentioned in the comments), another alternative would be to add a TruckProperties type (you could even add your default values to its 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;
}
}
Inside your Truck class, all you would do is manage an instance of the TruckProperties type, and let it do its reset.
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();
}
}
This certainly may be a lot of (unwanted) overhead for such a simple class, but in a bigger/more complex project it could be advantageous.
That's the thing about "best" practices... a lot of times, there's no silver bullet, but only recommendations you must take with skepticism and your best judgement as to what applies to you in a particular case.
I solved a similar problem with reflection. You can use source.GetType().GetProperties() to get a list of all properties which belong to the object.
Although, this is not always a complete solution. If your object implements several interfaces, you will also get all those properties with your reflection call.
So I wrote this simple function which gives us more control of which properties we are interested in resetting.
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.");
}
}
}
And it's use:
// 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));
You'd probably need to save the values off in private fields, so that they can be restored later. Maybe something like this:
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;
}
}
You're essentially looking for the State Design Pattern
If you want a specific past "state" of your object you can create a particular save point to return every time you want. This also let you have a diferent state to backup for everey instance that you create. If you class has many properties who are in constant change, this could be your 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));
}
}
If you aren't using a Code Generator or a Designer that would conflict, another option is to go through C#'s TypeDescriptor stuff, which is similar to Reflection, but meant to add more meta information to a class than Reflection could.
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);
}
}
}
Note that ResetValue will also reset to a shadowed property if one exists. The priority of which option is selected is explained in the docs:
This method determines the value to reset the property to in the following order of precedence:
There is a shadowed property for this property.
There is a DefaultValueAttribute for this property.
There is a "ResetMyProperty" method that you have implemented, where "MyProperty" is the name of the property you pass to it.
You may represent an object state as a struct or record struct and then set the state to the default value in the Reset method like this:
public class Truck {
record struct State(string Name, int Tires);
private static readonly State _defaultState = new("Super Truck", 4);
private State _state = _defaultState;
public string Name => _state.Name;
public int Tires => _state.Tires;
public Truck() {}
public void ResetTruck() => _state = _defaultState;
}
It is probably the fastest way as well.
Also, a record struct will give you the trivial implementations of the ToString, Equals, GetHashCode.