Как сбросить члены класса Class без воссоздания нового экземпляра? - PullRequest
2 голосов
/ 03 апреля 2020

Я бы хотел Reset, чтобы по умолчанию были установлены значения всех членов моего экземпляра класса CommunicationErrorsDetails. Этот класс является частью вложенного класса MyNestedClassInstance.

Это то, что я хотел бы сделать:

MyNestedClassInstance.CommunicationErrorsDetails.Reset()

Это образец моего вложенного класса MyNestedClass, экземпляр которого 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
    ...
}

Как мне достичь Reset() без необходимости заново создавать новый экземпляр и без необходимости сбрасывать вручную все элементы, которые могут быть разных типов?

Все члены являются простыми типами (не классами).

Более того, я не могу изменить структуру всех классов одного типа, потому что у нас несколько лет структурированный код, подобный этому.

Спасибо за вашу помощь. Привет

Ответы [ 4 ]

2 голосов
/ 06 апреля 2020

Один из вариантов - воспользоваться тем, что struct с. NET могут легко сбросить себя, назначив this.

Пример кода ниже (также доступен на * 1005). *https://dotnetfiddle.net/zhfjcg). Ключевой бит:

public void Reset()
{
    this = new TheData();
}

Это приведет к сбросу / повторной инициализации объекта TheData, так что все поля будут сброшены до значений по умолчанию.

В примере кода также используются свойства для предоставления данные структуры в CommunicationErrorsDetailsType. В этом нет особой необходимости, но, скорее всего, это хорошая идея.

Кроме того, этот метод ускоряет клонирование намного (против MemberwiseClone), предполагая, что содержимое struct don ' не может содержать ничего с причудливыми требованиями к клонированию, поскольку присвоение struct новой переменной будет «автоматически» клонировать ее.

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);
        }
    }
}
2 голосов
/ 03 апреля 2020

Просто чтобы предложить другую идею. Используйте косвенное указание.

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

Затем используйте его как:

instance.IntValue1 = 123;
instance.IntValue2 = 234;
instance.DoubleValue1 = 0.5;

instance.Reset();
1 голос
/ 03 апреля 2020

Если вы согласны с преобразованием полей publi c в свойства (что в любом случае рекомендуется), вы можете использовать внутренний словарь для хранения значений:

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

Затем «сброс» экземпляр просто однострочный

internal void Reset() { _values.Clear(); }
1 голос
/ 03 апреля 2020

Используя Reflexion, вы можете достичь того, чего хотите, и с помощью 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);
    }
}
...