Заставить входные параметры функции быть неизменными? - PullRequest
0 голосов
/ 02 апреля 2020

Я потратил большую часть двух дней, пытаясь отследить ошибку, оказалось, что я случайно изменил значения, которые были предоставлены в качестве входных данных для функции.

    IEnumerable<DataLog>
    FilterIIR(
        IEnumerable<DataLog> buffer
    ) {
        double notFilter = 1.0 - FilterStrength;

        var filteredVal = buffer.FirstOrDefault()?.oilTemp ?? 0.0;
        foreach (var item in buffer)
        {
            filteredVal = (item.oilTemp * notFilter) + (filteredVal * FilterStrength);

            /* Mistake here!
            item.oilTemp = filteredValue;
            yield return item;
            */

            // Correct version!
            yield return new DataLog()
            {
                oilTemp = (float)filteredVal,
                ambTemp = item.ambTemp,
                oilCond = item.oilCond,
                logTime = item.logTime
            };
        }
    }

Мое программирование предпочтительным языком обычно является C# или C ++, в зависимости от того, что, на мой взгляд, лучше соответствует требованиям (это часть более крупной программы, которая лучше подходит C#) ...

Теперь в C ++ я был бы возможность избежать такой ошибки, принимая постоянные итераторы, которые не позволяют вам изменять значения по мере их извлечения (хотя мне может понадобиться создать новый контейнер для возвращаемого значения). Я провел небольшой поиск и не могу найти простой способ сделать это в C#, кто-нибудь знает по-другому?

Я думал, что смогу создать класс IReadOnlyEnumerable<T>, который займет IEnumerable как конструктор, но потом я понял, что если он не сделает копию значений при их извлечении, это не будет иметь никакого эффекта, потому что базовое значение все еще можно изменить.

Есть ли способ Я мог бы быть в состоянии защитить от таких ошибок в будущем? Какой-нибудь класс-обертка, или даже если это небольшой фрагмент кода в верхней части каждой функции, которую я хочу защитить, все будет в порядке на самом деле.

Единственный разумный подход, о котором я могу подумать в данный момент, это ' Я буду работать над определением ReadOnly версии каждого нужного мне класса, а затем иметь версию, не предназначенную только для чтения, которая наследует и перегружает свойства и добавляет функции для обеспечения изменяемой версии того же класса.

1 Ответ

1 голос
/ 02 апреля 2020

Проблема здесь не совсем в IEnumerable. IEnumerable s на самом деле неизменны . Вы не можете добавлять или удалять вещи из них. Изменяемый класс DataLog.

Поскольку DataLog является ссылочным типом, item содержит ссылку на исходный объект, а не на копию объекта. Это, плюс тот факт, что DataLog является изменяемым, позволяет вам изменять передаваемые параметры.

Таким образом, на высоком уровне вы можете:

  • сделать копию DataLog, или;
  • сделать DataLog неизменным

или и то, и другое ...

То, что вы сейчас делаете, - это "сделать копию * 1025" *». Еще один способ сделать это - изменить DataLog с class на struct. Таким образом, вы всегда будете создавать его копию при передаче ее методам (если только вы не пометите параметр как ref). Поэтому будьте осторожны при использовании этого метода, потому что он может молча сломать существующие методы, которые предполагают передачу по ссылке semanti c.

. Вы также можете сделать DataLog неизменным. Это означает удаление всех сеттеров. При желании вы можете добавить методы с именем WithXXX, которые возвращают копию объекта, отличающегося только одним свойством. Если вы решили сделать это, ваш FilterIIR будет выглядеть следующим образом:

yield return item.WithOilTemp(filteredVal);

Единственный разумный подход, который я могу придумать на данный момент, который будет работать, это определить версию ReadOnly. каждого нужного мне класса, а затем иметь версию, не предназначенную только для чтения, которая наследует и перегружает свойства и добавляет функции для обеспечения изменяемой версии того же класса.

На самом деле вам не нужно это делать , Обратите внимание, как List<T> реализует IReadOnlyList<T>, хотя List<T> явно изменчив. Вы можете написать интерфейс под названием IReadOnlyDataLog. Этот интерфейс будет иметь только геттеры из DataLog. Затем FilterIIR примите IEnumerable<IReadOnlyDataLog> и DataLog орудие IReadOnlyDataLog. Таким образом, вы не будете случайно мутировать объекты DataLog в FilterIIR.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...