I'm a beginner in C# and I have a particular question that is related in inheritance and polymorphism based on using classes. The assignment that I'm working is a bank account and that I have to use three classes-one is a base class while the other two are derived classes.
The base class is known as "BankAccount" and the two derived classes are"CheckingAccount" and "SavingsAccount". Furthermore, I have declared objects for "CheckingAccount" and "SavingAccount" and that they are stored in a List class. Afterwards, I'm trying to populate my assignment's GUI's labels accordingly based from the derived class's properties by using a combobox's "SelectedIndexChange" event.
The problem is that somehow the program doesn't check if the list class's stored elements are recognized as a particular type. Below of this contains my code for the program and the classes.
private void comboBoxAccountNumber_SelectedIndexChanged(object sender, EventArgs e)
{
if (selectedBankAccount[0] is CheckingAccount)
{
labelOwnerID.Text = selectedBankAccount[0].AccountNumber;
labelBalance.Text = selectedBankAccount[0].Balance.ToString("c");
}
else if (selectedBankAccount[1] is CheckingAccount)
{
labelOwnerID.Text = selectedBankAccount[1].AccountNumber;
labelBalance.Text = selectedBankAccount[1].Balance.ToString("c");
}
}
List<BankAccount> selectedBankAccount = new List<BankAccount> ();
SavingsAccount savs1_Account;
CheckingAccount chek1_Account;
SavingsAccount savs2_Account;
CheckingAccount chek2_Account;
private void FormBankOfYourSelf_Load(object sender, EventArgs e)
{
savs1_Account = new SavingsAccount("0001", "31-1000", 100m, 0.01);
chek1_Account = new CheckingAccount("0001", "44-1000", 250m, true);
savs2_Account = new SavingsAccount("0002", "31-1001", 1000m, 0.0125);
chek2_Account = new CheckingAccount("0002", "44-1001", 500m, false);
selectedBankAccount.Add(chek1_Account);
selectedBankAccount.Add(chek2_Account);
comboBoxAccountNumber.Items.Add(selectedBankAccount[0].AccountNumber);
comboBoxAccountNumber.Items.Add(selectedBankAccount[1].AccountNumber);
}
public abstract class BankAccount
{
// Fields - The data we want to store
// Naming convention for fields is to use underscore before the name
protected string _customerId;
protected string _accountNumber;
protected decimal _balance;
//Properties - Allow access to fields (Get/Set)
// Get = read access
// Set = write (modify) access
public string CustomerId
{
get { return _customerId; }
set { _customerId = value; }
}
public string AccountNumber
{
get { return _accountNumber; }
set { _accountNumber = value; }
}
public decimal Balance
{
get { return _balance; }
}
// Methods - The action or behaviors the class can do
// almost always, define the constructor and ToString methods
// Constructor creates (instantiates a new object)
public BankAccount(string customerId, string accountNumber, decimal initialBalance)
{
//fields are set = to the parameters(inputs from the form)
_customerId = customerId;
_accountNumber = accountNumber;
_balance = initialBalance;
}
public abstract bool Deposit(decimal depositAmount);
//{
// // If the depositAmount is less then 0 then RETURN false
// if (depositAmount <= 0)
// {
// return false;
// }
// // Otherwise, complete the deposit and RETURN true
// _balance += depositAmount;
// return true;
//}
public abstract bool Withdraw(decimal withdrawAmount);
//{
// // If the withdrawAmount is greater than the balance or less then or equal to 0
// // then RETURN false (don't allow withdrawal)
// if (withdrawAmount > _balance || withdrawAmount <= 0)
// {
// return false;
// }
// // Otherwise, complete the withdrawal and return true
// _balance -= withdrawAmount;
// return true;
//}
public class CheckingAccount : BankAccount
{
//private string _customerID;
//private string _accountNum;
//private decimal _initBalance;
private bool _overdraftProtection;
public CheckingAccount(string customerId, string accountNum, decimal initialBalance, bool overDraft)
:base(customerId, accountNum, initialBalance)
{
//_customerID = customerId;
//_accountNum = accountNum;
//_initBalance = initialBalance;
_overdraftProtection = overDraft;
}
public bool OverDraftProtection
{
get { return _overdraftProtection; }
set { _overdraftProtection = value; }
}
public override bool Deposit(decimal depositAmount)
{
if(depositAmount > 0)
{
_balance += depositAmount;
return true;
}
else
{
return false;
}
}
public override bool Withdraw(decimal withdrawAmount)
{
if(withdrawAmount <= _balance || withdrawAmount > 0 )
{
_balance -= withdrawAmount;
return true;
}
else
{
return false;
}
}
}
**Update: I noticed that when I run my program, even though I select different items within the combobox, it doesn't change the values to a selected item accordingly. Below is my program running with different items being selected.
First selected item in combobox
Second selected item in combobox
Your list is of type BankAccount so you need to cast the object to the specific inherited class you want to work with before the properties will become available
Using the is keyword and then the as keyword is expensive performance wise because you are basically casting twice. Best to use the as keyword and test for null
CheckingAccount account = selectedBankAccount[0] as CheckingAccount
if(account != null)
{
//account is of type CheckingAccount so the additional
//CheckingAccount properties should be available via the account variable
}
From posted comments it turns out the problem is that the code is not selecting the BankAccount selected in the ComboBox. Not knowing how the ComboBox is populated. The easiest way is to use the selected index assuming comboBoxAccountNumber is populated with the selectedBankAccount collection.
private void comboBoxAccountNumber_SelectedIndexChanged(object sender, EventArgs e)
{
BackAccount account = selectedBankAccount[comboBoxAccountNumber.SelectedIndex]
labelOwnerID.Text = account.AccountNumber;
labelBalance.Text = account.Balance.ToString("c");
}
Related
This question already has answers here:
Check inside method whether some optional argument was passed
(10 answers)
Closed 2 years ago.
I hope someone can help me or give me an information if that is even possible...
I want to check in an Named-Method which parameters are really set and passed to it.
It would be possible to make an Dictionary and pass the parameter by an KeyValue-Pair to the Method, but is there an other solution?
Can i check the current method in the stack-trace and collect the current set argmuents of that method or something?
For better understanding, i created an example to visualize the issue:
class Program
{
static void Main(string[] args)
{
TestClass newC = new TestClass("Init", 10);
newC.ToString(); //{varStr: "Init"; varInt: 10}
newC.update(vStr: "ok");
newC.ToString(); //{varStr: "ok"; varInt: 0}
// !!! but should have {varStr: "ok"; varInt: 10} !!!
Console.WriteLine("<-- press any key to exit -->");
Console.ReadKey();
}
}
class TestClass
{
string varStr;
int varInt;
public TestClass(string vStr, int vInt)
{
varStr = vStr;
varInt = vInt;
}
public void update(string vStr = default, int vInt = default)
{
//here check it if vStr-Param was set and set varStr only if it was passed to it!
//TODO
varStr = vStr;
//here check it if vInt-Param was set and set varInt only if it was passed to it!
//TODO
varInt = vInt;
}
public override string ToString()
{
Console.WriteLine($"TestClass: varStr: {varStr}; varInt: {varInt};");
return null;
}
}
Is there any way to achieve this, like the way i think?
EDIT:
Use default value and check it!
The default value can also be set over this Method! So an solution
with checking the default value and only if that is equal than we set
the value, is no option for us.
We have also an "custom-class" which we give over that method and it
could also be nullable.
Use Overload methods!
The Problem is that this class has not 2 variables, it has over 15 and
it would be an big overhead to write each overload method.
SOLUTION:
I found a Solution without declare default values... so every Value is able to be set!
class TestClass
{
string varStr;
int varInt;
TestClass1 varCustomClass;
public TestClass(string vStr, int vInt, TestClass1 vCustomClass)
{
varStr = vStr;
varInt = vInt;
varCustomClass = vCustomClass;
}
public void update(Opt<int> varInt = default(Opt<int>),
Opt<string> varStr = default(Opt<string>),
Opt<TestClass1> varCustomClass = default(Opt<TestClass1>))
{
if (varStr.HasValue)
{
this.varStr = varStr.Value;
}
if (varInt.HasValue)
{
this.varInt = varInt.Value;
}
if (varCustomClass.HasValue)
{
this.varCustomClass = varCustomClass.Value;
}
}
public override string ToString()
{
Console.WriteLine($"TestClass3: varStr: {varStr}; varInt: {varInt}; varCustomClass: {varCustomClass};");
return null;
}
}
public struct Opt<T>
{
public Opt(T value)
{
_value = value;
_hasValue = true;
}
public static explicit operator T(Opt<T> optional)
{
return optional._value;
}
public static implicit operator Opt<T>(T value)
{
return new Opt<T>(value);
}
T _value;
public T Value
{
get { return _value; }
}
bool _hasValue;
public bool HasValue
{
get { return _hasValue; }
}
}
Might your string also be null? If not, you can check if it's null. For your int, you can use a nullable value type:
public void update(string vStr = null, int? vInt = null)
{
if(vStr != null)
{
varStr = vStr;
}
if(vInt != null)
{
varInt = vInt.Value;
}
}
If you have only a few parameters and they all have different types, overloaded methods are also a good solution:
public void update(string vStr)
{
varStr = vStr;
}
public void update(int vInt)
{
varInt = vInt;
}
public void update(string vStr, int vInt)
{
update(vStr);
update(vInt);
}
EDIT: Question Reconstructed.
OK, I have revisited my get and set methods, but I am still very unclear on how it all works.
What I want to achieve is the Model is populated by the Controller, from the values that it takes form the form. This is then sent to the Db_Facade, which compares the uName and uPwd, and if they are equal returns the ACCESS, which will be set for the entire scope of the program.
I don't know if the get and set declarations are done correctly, or if they can be bunched together (If this is possible it would be great because I will be using this for much larger collections of data), and I'm pretty sure I'm implementing them wrong as well.
If you can help, my knowledge of Accessors is incredibly limited.
Here is my Compare Login method in my Controller:
public static void Compare_Login(User_Login_View Login_View)
{
User_Model getACCESS = new User_Model(); // Creates a new oject of User_Model
getACCESS.Name = Login_View.txtUsername.Text; //Populates the Model from the Login View
getACCESS.Pwd = Login_View.txtPassword.Text;
if (getACCESS.ACCESSLEVEL > 0)
{
Login_View.Close();
}
else
{
Login_View.lblError.Visible = true;
}
Login_View.Menu.SetMenuView();
}
Here is my Model:
public class User_Model
{
public string Name
{
get
{
return Db_Facade.uName;
}
set
{
Db_Facade.uName = value;
}
}
public string Pwd
{
get
{
return Db_Facade.uPwd;
}
set
{
Db_Facade.uPwd = value;
}
}
public int ACCESSLEVEL
{
get
{
return Db_Facade.ACCESS;
}
set
{
Db_Facade.ACCESS = value;
}
}
}
Here is the dummy database comparison:
class Db_Facade
{
public static string uName;
public static string uPwd;
public static string cPwd;
public static int ACCESS;
public static void getLoginACCESS()
{
uName = "paul";
uPwd = "pwd";
ACCESS = 1;
/* I get a "getACCESS does not exist" error here
if (uName == getACCESS.Name && uPwd == getACCESS.Pwd)
{
getACCESS.ACCESSLEVEL = ACCESS;
}
else
{
getACCESS.ACCESSLEVEL = 0;
}
*/
}
}
I don't know if it's needed, but here is my View
public partial class User_Login_View : Form
{
public Menu_View Menu { get; set; }
public User_Login_View()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
User_Controller.Compare_Login(this);
}
}
2 Questions / Hints
1.) Where do you call your getLoginACCESS() ?
2.) Why do you think Db_Facade is able to access getACCESSfrom your class User_Controller?
a solution would be to modyfie your getLoginACCESS() to getLoginACCESS(User_Model getACCESS) and than call it in your Compare_Login(User_Login_View Login_View) befor your if like Db_Facade.etLoginACCESS(getACCESS);
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:)
}
}
The Problem:
All tables in our database have CreatedDate, CreatedBy, ChangedDate, ChangedBy fields which I want to be set automatically when Saving / Updating an ActiveRecord entity.
My first try was to override the Save() and Update() methods. But these methods only get called when I do a direct Save() or Update() on the entity. They are not being called in a Master - Detail scenario where I call Save() only on the master.
Next try were the OnSave() and OnUpdate() methods, but here changes in the fields were not persisted in the database.
Finally I tried the BeforeSave() method. But this method is not called when updating.
The Question:
How can set these CreatedDate, CreatedBy, ChangedDate, ChangedBy fields automatically during a Save() or Update()?
To modify data as you you want you have to override the BeforeSave method like this:
protected override bool BeforeSave(IDictionary state)
{
bool retval = base.BeforeSave(state);
state["Password"] = Global.Encrypt("password");
return retval;
}
And finally save your instance:
protected void btnSave_Click(object sender, EventArgs e)
{
try
{
qfh.User user = null;
user = new qfh.User();
user.UserName = txtUserName.Text;
user.Name = txtName.Text;
user.IsAdministrator = cboIsAdministrador.SelectedValue == "Yes";
user.IsActive = cboIsActive.SelectedValue == "Yes";
user.SaveCopy();
}
catch (Exception ex)
{
ex = Utilities.GetInnerException(ex);
JSLiteral.Text = Utilities.GetFormattedExceptionMessage(ex);
}
}
I usually use SaveCopy() to make use of the overriden method FindDirty(object id, IDictionary previousState, IDictionary currentState, NHibernate.Type.IType[] types) to get the previous values of the class.
Hope it helps.
Use BeforeSave() for saving and OnFlushDirty() for updating.
you could do like this
[ActiveRecord("PostTable")]
public class Post : ActiveRecordBase<Post>
{
private int _id;
private DateTime _created;
[PrimaryKey]
public int Id
{
get { return _id; }
set { _id = value; }
}
[Property("created")]
public DateTime Created
{
get { return _created; }
set { _created = value; }
}
private void BeforeUpdate()
{
// code that run before update
Created = DateTime.Now;
}
public override void Update()
{
BeforeUpdate();
base.Update();
}
I had a same problem and solved it this way:
I use OnUpdate() and OnSave(). As you mentioned this solution does not work with master detail scenarios. For this I set parent of each child explicitly. Note following codes:
[ActiveRecord(Lazy = true)]
public class Lookup : ActiveRecordBase<Lookup>
{
[HasMany(typeof(LookupItem), Cascade = ManyRelationCascadeEnum.All)]
public virtual IList Items { set; get; }
//other properties...
}
[ActiveRecord(Lazy = true)]
public class LookupItem : ActiveRecordBase<LookupItem>
{
[BelongsTo("Lookup_id")]
public virtual Lookup ContainerLookup { set; get; }
//other properties...
}
void SaveLookup()
{
Lookup lookup = GetLookup();
LookupItem lookupItem = new LookupItem()
{
Title = LookupItemName,
ContainerLookup = lookup
};
lookup.Items.Add(lookupItem);
lookup.Save();
}
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.