Reputation:
I'd like to Reset
to there default values all of the members of my class instance CommunicationErrorsDetails
.
This class is part of a nested class MyNestedClassInstance
.
This is what I'd like to do :
MyNestedClassInstance.CommunicationErrorsDetails.Reset()
This a sample of my nested class MyNestedClass
which instance is MyNestedClassInstance
:
public class MyNestedClass : ICloneable
{
...
/// <summary>
/// Communication errors count
/// </summary>
public class CommunicationErrorsDetailsType : ICloneable
{
public int RetryCount;
public int CRCErrorCount;
public int DataBytesNotExpectedCount;
public int TooMuchDataReceivedCount;
public int ResponseDataAddressNotEqualCount;
public int BytesReceivedInCommunicationStateStartCount;
public int BytesReceivedInCommunicationStateSendFrameCount;
public int BytesReceivedInCommunicationStateDataResponseReceivedCount;
public int ExceptionCount;
public int NakcReceivedCount;
public int AckTimeoutCount;
public int DataTimeoutCount;
public double DataTimeoutRate;
public bool HasCommunicationErrors
{
get => RetryCount > 0
|| CRCErrorCount > 0
|| DataBytesNotExpectedCount > 0
|| TooMuchDataReceivedCount > 0
|| ResponseDataAddressNotEqualCount > 0
|| BytesReceivedInCommunicationStateStartCount > 0
|| BytesReceivedInCommunicationStateSendFrameCount > 0
|| BytesReceivedInCommunicationStateDataResponseReceivedCount > 0
|| ExceptionCount > 0
|| NakcReceivedCount > 0
|| AckTimeoutCount > 0
|| DataTimeoutCount > 0;
}
public object Clone()
{
return MemberwiseClone();
}
internal void Reset()
{
// ... ?
}
}
public CommunicationErrorsDetailsType CommunicationErrorsDetails = new CommunicationErrorsDetailsType();
...
// Other nested classes
...
}
How can I achieve Reset()
without having to recreate a new instance and without having to reset manually all members that can be of different types ?
All members are simple types (not classes).
Furthermore, I cannot change the structure of all classes of same type because we have several years of code structured like this.
Thank you for your help. Regards
Upvotes: 1
Views: 739
Reputation: 23820
One option is to take advantage of the fact that struct
s in .NET can easily reset themselves by assigning this
.
An example code sample is below (also available at https://dotnetfiddle.net/zhfjcg). The key bit is:
public void Reset()
{
this = new TheData();
}
This will reset / reinitialise the TheData
object - so that all fields are reset back to their default.
The code sample also uses properties to expose the struct data in CommunicationErrorsDetailsType
. That isn't strictly necessary, but is likely a good idea.
Additionally, this technique makes cloning much faster (vs MemberwiseClone
) assuming the contents of the struct
don't contain anything with fancy cloning requirements, since assigning the struct
to a new variable will "automatically" clone it.
using System;
namespace WhatEver
{
public struct TheData
{
public int RetryCount;
public int CRCErrorCount;
public int DataBytesNotExpectedCount;
public int TooMuchDataReceivedCount;
public int ResponseDataAddressNotEqualCount;
public int BytesReceivedInCommunicationStateStartCount;
public int BytesReceivedInCommunicationStateSendFrameCount;
public int BytesReceivedInCommunicationStateDataResponseReceivedCount;
public int ExceptionCount;
public int NakcReceivedCount;
public int AckTimeoutCount;
public int DataTimeoutCount;
public double DataTimeoutRate;
public void Reset()
{
this = new TheData();
}
}
public class CommunicationErrorsDetailsType
{
private TheData data;
public int DataTimeoutCount
{
get
{
return data.DataTimeoutCount;
}
set
{
data.DataTimeoutCount = value;
}
}
public bool HasCommunicationErrors
{
get => data.RetryCount > 0
|| data.CRCErrorCount > 0
|| data.DataBytesNotExpectedCount > 0
|| data.TooMuchDataReceivedCount > 0
|| data.ResponseDataAddressNotEqualCount > 0
|| data.BytesReceivedInCommunicationStateStartCount > 0
|| data.BytesReceivedInCommunicationStateSendFrameCount > 0
|| data.BytesReceivedInCommunicationStateDataResponseReceivedCount > 0
|| data.ExceptionCount > 0
|| data.NakcReceivedCount > 0
|| data.AckTimeoutCount > 0
|| data.DataTimeoutCount > 0;
}
public object Clone()
{
return MemberwiseClone();
}
internal void Reset()
{
data.Reset();
}
}
public class ToRun
{
public static void Main()
{
var hereWeGo = new CommunicationErrorsDetailsType();
hereWeGo.DataTimeoutCount = 4;
Console.WriteLine(hereWeGo.DataTimeoutCount);
hereWeGo.Reset();
Console.WriteLine(hereWeGo.DataTimeoutCount);
}
}
}
Upvotes: 1
Reputation: 152501
If you're OK with redesigning your public fields to properties (which is a best practice anyways) you can use an internal dictionary to store the values:
private Dictionary<string, int> _values = new Dictionary<string, int>();
private int get_val(string key)
{
int output;
_values.TryGetValue(key, out output);
return output;
}
public int RetryCount {get {return get_val("RetryCount");} set {_values["RetryCount"] = value;}
public int CRCErrorCount {get {return get_val("CRCErrorCount");} set {_values["CRCErrorCount"] = value;}
public int DataBytesNotExpectedCount {get {return get_val("CRCErDataBytesNotExpectedCount rorCount");} set {_values["DataBytesNotExpectedCount "] = value;}
...
Then "resetting" an instance is just a one-liner
internal void Reset() { _values.Clear(); }
Upvotes: 0
Reputation:
Using Reflexion you can achieve what you want and with the help of DefaultExpression Class
internal void Reset()
{
// Retrieves only the fields of this Class
var bindingFlags = BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public;
List<FieldInfo> members = this.GetType()
.GetFields(bindingFlags)
.Where(f => f.MemberType == MemberTypes.Field)
.Where(value => value != null)
.ToList();
// Set all fields to its default type value
for (int i = 0; i < members.Count(); i++)
{
// This expression represents the default value of a type
// (0 for integer, null for a string, etc.)
Expression defaultExpr = Expression.Default(typeof(byte));
// The following statement first creates an expression tree,
// then compiles it, and then executes it.
var defaultValue = Expression.Lambda<Func<byte>>(defaultExpr).Compile()();
// Set the default value
members[i].SetValue(this, defaultValue);
}
}
Upvotes: 0
Reputation: 10035
Just to offer another idea. Use an indirection.
class Instance
{
public Holder<int> IntValue1;
public Holder<int> IntValue2;
public Holder<double> DoubleValue1;
public void Reset()
{
Holder.Reset();
}
internal class Holder
{
public static event EventHandler OnReset;
public static void Reset()
{
OnReset?.Invoke(null, null);
}
}
public class Holder<T>
{
private Holder()
{
Holder.OnReset += (_, __) => Value = default(T);
}
public T Value { get; set; }
public static implicit operator T(Holder<T> holder)
{
return holder.Value;
}
public static implicit operator Holder<T>(T value)
{
return new Holder<T>() { Value = value };
}
}
}
Then use it like:
instance.IntValue1 = 123;
instance.IntValue2 = 234;
instance.DoubleValue1 = 0.5;
instance.Reset();
Upvotes: 0