user13051425
user13051425

Reputation:

How to Reset members of an instance Class without recreate a new instance?

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

Answers (4)

mjwills
mjwills

Reputation: 23820

One option is to take advantage of the fact that structs 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

D Stanley
D Stanley

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

user12805184
user12805184

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

weichch
weichch

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

Related Questions