Обойти все записи как элементы коллекции этотобъект
Перейти к содержимому

Обойти все записи как элементы коллекции этотобъект

  • автор:

Коллекции значений в 1С

В данной статье пойдет речь о том, какие коллекции значений есть во встроенном языке 1С, их особенности и для чего они применяются.

В материале освещены сразу два вопроса из собеседования программиста 1С:

  1. Чем отличается структура от соответствия?
  2. Можно ли сделать запрос по таблице значений?

Разработчику во встроенном языке доступны следующие объекты, описывающие коллекции:

Содержание скрыть

Массив

Объект описывает коллекцию значений массива. У каждого элемента есть индекс. В 1С можно создавать многомерные массивы, например так:

Массив1 = Новый Массив(10,2,4) 

В массиве можно искать элементы методом Найти(), но поскольку у массива нет индексов данный поиск выполняется не быстро. Метода сортировки массива разработчики 1С не добавили. Возможно, это сделано не просто так, т.к. сортировать можно другие коллекции

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

Фиксированный массив

Тот же массив, но его элементы нельзя изменить, т.е. у него есть только методы Получить(), Найти(), ВГраница() и Количество(). Создается на основе обычного массива.

Данный объект как правило используется в свойствах интерфейсных объектов (элементов управления)

Список значений

Список значений можно представить в качестве следующей таблицы значений:

Список = Новый ТаблицаЗначений; Список.Колонки.Добавить("Пометка", Новый ОписаниеТипов("Булево")); Список.Колонки.Добавить("Значение"); Список.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка")); Список.Колонки.Добавить("Картинка", Новый ОписаниеТипов("Картинка")); 

Как видно из структуры объекта, в поле «Значение» может быть любой объект.

Данный объект, в отличие от массива, уже содержит методы сортировки как по полю Значение, так и по полю Представление. Список значений можно заполнить элементами массива, используя метод ЗагрузитьЗначения().

Списки используются для передачи параметров в запрос, в качестве объекта сравнения в СКД и построителе, а также в отборах в интерфейсе.

Данный объект может быть отображен в интерфейсе, кроме того он имеет методы позволяющие показать диалог установки отметок в списке, диалог выбора значения из списка.

Структура

Структура представляет собой коллекцию пар КлючИЗначение. При этом ключ может быть только строковым и должен удовлетворять требованиям, предъявляемым к именованию переменных встроенного языка, т.е. ключом не может выступать строка «123ключ». К значениям структуры можно обращаться как к свойствам объекта. При этом ключ используется как имя свойства.

Пары Ключ-значение можно обойти циклом Для каждого … Из … Цикл, проверить существует ли свойство(ключ) структуры можно методом Свойство().

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

Соответствие

Соответствие как и структура представляет собой коллекцию пар КлючИЗначение. При этом, у соответствия в качестве ключа может выступать любое значение! Рекомендуется, чтобы в качестве ключа выступало значение примитивного типа или другого типа, значение которого может только присваиваться, но не может менять свое содержимое. Еще одной особенность соответствия является то, что это индексированная коллекция, т.е. поиск значения по ключу осуществляется очень быстро.

Пары Ключ-значение можно обойти циклом Для каждого … Из … Цикл.

Соответствие обычно используется в тех случаях, когда необходимо заполнить таблицу соответствия одного значения другому, причем размеры этой таблицы могут быть достаточно большими. Также очень удобно использовать соответствие в качестве кэша из-за высокой скорости поиска в нем.

Таблица значений

Таблица значений предназначена для хранения значений в табличном виде. Все основные операции с таблицей производятся именно через этот объект. Он позволяет манипулировать строками таблицы значений и предоставляет доступ к коллекции колонок. Колонки могут быть различных типов (в том числе множественных).

У таблицы значений есть метод Сортировать(), с помощью которого можно сортировать таблицу сразу по нескольким колонкам. С помощью метода Итог() можно сразу получить сумму колонки.

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

Таблица = Новый ТаблицаЗначений; Таблица.Колонки.Добавить("Значение");/pre> p>А такую можно:/p> pre>Таблица = Новый ТаблицаЗначений; Таблица.Колонки.Добавить("Пометка", Новый ОписаниеТипов("Булево")); Таблица.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка")); 

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

В управляемом приложении данный объект доступен только на сервере! Другими словами, попытка создать на клиенте равно как и передать таблицу значений с сервера на клиент приведет к ошибке.

Обойти все записи как элементы коллекции этотобъект

Описание:

Список значений — это не сохраняемый в базе данных объект, который позволяет строить для решения интерфейсных задач динамические наборы значений и манипулировать ими (добавлять, редактировать, удалять элементы, сортировать). Он может быть наполнен значениями любого типа, т.е. в одном списке типы хранимых значений могут быть разными. Одним из примеров использования данного объекта может служить организация выбора конкретного документа из списка возможных документов, сформированного по сложному алгоритму.

Элементы коллекции: ЭлементСпискаЗначений

Для объекта доступен обход коллекции посредством оператора Для каждого … Из … Цикл. При обходе выбираются элементы коллекции.

Возможно обращение к элементу коллекции посредством оператора [. ]. В качестве аргумента передается индекс элемента (нумерация с 0).

Свойства:

ДоступныеЗначения Применяется в случаях, когда требуется ограничить возможность задания значений для элементов списка некоторым подмножеством значений допустимого типа (если для списка установлено описание типа). Неопределено — ограничения отсутствуют. Установка этого свойства влияет на работу диалога редактирования списка значений, элементов управления «Таблица» и «Поле ввода», связанных со списком значений, а также на программное добавление-вставку нового элемента списка значений и установку значений элементам списков.
ТипЗначения Определяет тип (см. ОписаниеТипов) для значений, которые могут храниться в элементах данного списка значений.

Методы:

Вставить (Индекс, Значение, Представление, Пометка, Картинка) Вставляет новый элемент в список значений на позицию с указанным индексом.
ВыбратьЭлемент (Заголовок, Элемент) Вызывает окно для интерактивного выбора одного из элементов, входящих в список значений. Элемент — элемент, на который следует изначально спозиционировать список значений при интерактивном выборе. Если значением параметра не является элемент списка значений, входящий в данный список, позиционирования не произойдет.
ВыгрузитьЗначения () Создает массив и копирует в него значения элементов списка значений.
Добавить (Значение, Представление, Пометка, Картинка) Добавляет новый элемент в конец списка значений.
ЗагрузитьЗначения (МассивЗначений) Загружает список значений значениями из переданного массива. При этом все прежние элементы списка удаляются.
ЗаполнитьПометки (Пометка) Устанавливает пометку у всех элементов списка значений.
Индекс (Элемент) Получает индекс элемента в списке значений. Если не найдено, то возвращается -1.
Количество () Получает количество элементов списка значений.
НайтиПоЗначению (ИскомоеЗначение) Осуществляет поиск значения элемента списка значений. Если ни один элемент не хранит значения, равного искомому, то возвращается значение Неопределено.
НайтиПоИдентификатору (Идентификатор) Получает элемент списка значений по идентификатору. Если элемент не найден, то возвращается Неопределено.
ОтметитьЭлементы (Заголовок) Устанавливает или снимает (интерактивно) пометки у элементов списка значений. Возвращает Истина, если в диалоге нажата кнопка «ОК», Ложь — в противном случае.
Очистить () Очищает список значений, удаляя из него все элементы.
ПоказатьВыборЭлемента ( ОписаниеОповещенияОЗакрытии, Заголовок, Элемент) Вызывает окно для интерактивного выбора одного из элементов, входящих в список значений.
ПоказатьОтметкуЭлементов ( ОписаниеОповещенияОЗакрытии, Заголовок) Предназначен для интерактивной установки состояний пометок у элементов списка значений.
Получить (Индекс) Получает значение по индексу. Работает аналогично оператору [].
Сдвинуть (Элемент, Смещение) Сдвигает элемент списка значений на заданное количество позиций вперед или назад.
Скопировать () Создает копию списка значений.
СортироватьПоЗначению (Направление) Сортирует список значений в порядке возрастания или убывания хранимых элементами значений. См. пример ниже.
СортироватьПоПредставлению ( Направление) Сортирует список значений по возрастанию или по убыванию в алфавитном порядке представлений входящих в состав списка значений элементов. См. пример ниже.
Удалить (Элемент) Удаляет элемент из списка значений, где Элемент — индекс удаляемого элемента, либо сам элемент.

Конструкторы:

Новый СписокЗначений

Оглавление (нажмите, чтобы раскрыть)

  • Как создать список значений
  • Как добавить элемент в список значений
  • Как вставить элемент в список значений
  • Как обойти элементы списка значений
  • Как очистить список значений
  • Как узнать количество элементов списка значений, а также получить элемент списка по его индексу
  • Как найти элемент списка по его значению
  • Как узнать индекс элемента в списке
  • Как отсортировать список по его значениям
  • Как удалить элемент из списка значений
  • Как сдвинуть элемент списка значений
  • Как сделать копию списка
  • Как выгрузить значения списка в массив
  • Как загрузить значения списка из массива
  • Как сделать немодальный выбор значения из списка значений
  • Как сделать немодальную отметку значений из списка значений
  • Как сделать модальный выбор значения из списка
  • Как сделать модальную отметку значений из списка значений
  • Скачать и выполнить эти примеры на компьютере
&НаКлиенте Процедура ВыполнитьКод(Команда) /// Как создать список значений в 1с 8.3, 8.2 Список = Новый СписокЗначений; /// Как добавить элемент в список значений в 1с 8.3, 8.2 // параметры метода добавить: // - значение // - представление // - отметка (необ.) // - картинка (необ.) Список.Добавить( 1980, // значение элемента "Год рождения Васи" // представление ); Список.Добавить(1985, "Год рождения Юли"); // значения могут быть разных типов Список.Добавить("Полина", "Имя ребёнка"); /// Как вставить элемент в список значений в 1с 8.3, 8.2 // вставляем в позицию №2 (элементы нумеруются с 0) // элемент со значением 2010 и представлением // "Год рождения их совместной дочки" Список.Вставить(2, 2010, "Год рождения их совместной дочки"); /// Как обойти элементы списка значений в 1с 8.3, 8.2 Для Каждого Элемент Из Список Цикл Сообщить( Элемент.Представление + ": " + Строка(Элемент.Значение) ); КонецЦикла; /// Как очистить список значений в 1с 8.3, 8.2 Список.Очистить(); Список.Добавить("Понедельник"); Список.Добавить("Вторник"); Список.Добавить("Среда"); /// Как узнать количество элементов списка значений, а также /// получить элемент списка по его индексу в 1с 8.3, 8.2 // нумерация с нуля Для Индекс = 0 По Список.Количество() - 1 Цикл Сообщить(Список[Индекс]); КонецЦикла; /// Как найти элемент списка по его значению в 1с 8.3, 8.2 ЗначВторник = Список.НайтиПоЗначению("Вторник"); /// Как узнать индекс элемента в списке в 1с 8.3, 8.2 Сообщить(Список.Индекс(ЗначВторник)); // 1, так как нумерация с нуля /// Как отсортировать список по его значениям в 1с 8.3, 8.2 // было: понедельник, вторник, среда Список.СортироватьПоЗначению(НаправлениеСортировки.Убыв); // стало: среда, понедельник, вторник /// Как удалить элемент из списка значений в 1с 8.3, 8.2 // удаляем первый элемент // параметр: элемент списка // или индекс элемента // можно так Список.Удалить(Список[0]); // или так // Список.Удалить(0); /// Как сдвинуть элемент списка значений в 1с 8.3, 8.2 // сдвигаем нулевой элемент на одну позицию вперёд // было: понедельник вторник Список.Сдвинуть(0, 1); // стало: вторник понедельник /// Как сделать копию списка в 1с 8 КопияСписка = Список.Скопировать(); Цвета = Новый СписокЗначений; Цвета.Добавить("Красный"); Цвета.Добавить("Зелёный"); Цвета.Добавить("Синий"); /// Как выгрузить значения списка в массив в 1с 8.3, 8.2 МассивЦветов = Цвета.ВыгрузитьЗначения(); /// Как загрузить значения списка из массива в 1с 8.3, 8.2 Цвета.ЗагрузитьЗначения(МассивЦветов); КонецПроцедуры /// Как сделать немодальный выбор значения из списка /// значений в 1с 8.3, 8.2 &НаКлиенте Процедура КакСделатьНемодальныйВыборЗначения(Команда) Цвета = Новый СписокЗначений; Цвета.Добавить("Красный"); Цвета.Добавить("Зелёный"); Цвета.Добавить("Синий"); // процедура ПослеВыбораЭлемента определена чуть ниже ОповещениеПослеВыбораЭлемента = Новый ОписаниеОповещения( "ПослеВыбораЭлемента", ЭтотОбъект ); Цвета.ПоказатьВыборЭлемента( ОповещениеПослеВыбораЭлемента, "Выберите любимый цвет" ); КонецПроцедуры &НаКлиенте Процедура ПослеВыбораЭлемента(Элемент, Параметры) Экспорт Если Элемент <> Неопределено Тогда Сообщить(Элемент.Значение); КонецЕсли; КонецПроцедуры /// Как сделать немодальную отметку значений из списка /// значений в 1с 8.3, 8.2 &НаКлиенте Процедура КакСделатьНемодальнуюОтметкуЗначений(Команда) Цвета = Новый СписокЗначений; Цвета.Добавить("Красный"); Цвета.Добавить("Зелёный"); Цвета.Добавить("Синий"); // процедура ПослеОтметкиЭлементов определена чуть ниже ОповещениеПослеОтметкиЭлементов = Новый ОписаниеОповещения( "ПослеОтметкиЭлементов", ЭтотОбъект ); Цвета.ПоказатьОтметкуЭлементов( ОповещениеПослеОтметкиЭлементов, "Выберите любимые цвета" ); // а вот так можно разом установить все отметки // списка в определенное значение Цвета.ЗаполнитьПометки(Истина); КонецПроцедуры &НаКлиенте Процедура ПослеОтметкиЭлементов(Элементы, Параметры) Экспорт Если Элементы <> Неопределено Тогда Для Каждого Цвет Из Элементы Цикл Если Цвет.Пометка Тогда Сообщить(Цвет.Значение); КонецЕсли; КонецЦикла; КонецЕсли; КонецПроцедуры /// Как сделать модальный выбор значения из списка в 1с 8.3, 8.2 &НаКлиенте Процедура КакСделатьМодальныйВыборЗначения(Команда) Цвета = Новый СписокЗначений; Цвета.Добавить("Красный"); Цвета.Добавить("Зелёный"); Цвета.Добавить("Синий"); ВыбЦвет = Цвета.ВыбратьЭлемент("Выберите любимый цвет"); Если ВыбЦвет <> Неопределено Тогда Сообщить(ВыбЦвет.Значение); КонецЕсли; КонецПроцедуры /// Как сделать модальную отметку значений из списка /// значений в 1с 8.3, 8.2 &НаКлиенте Процедура КакСделатьМодальнуюОтметкуЗначений(Команда) Цвета = Новый СписокЗначений; Цвета.Добавить("Красный"); Цвета.Добавить("Зелёный"); Цвета.Добавить("Синий"); Если Цвета.ОтметитьЭлементы("Выберите любимые цвета") Тогда Для Каждого Цвет Из Цвета Цикл Если Цвет.Пометка Тогда Сообщить(Цвет.Значение); КонецЕсли; КонецЦикла; КонецЕсли; // а вот так можно разом установить все отметки // списка в определенное значение Цвета.ЗаполнитьПометки(Истина); КонецПроцедуры /// Скачать и выполнить эти примеры на компьютере

Список значений в языке 1С 8.3, 8.2 (в примерах)

С уважением, Владимир Милькин (преподаватель школы 1С программистов и разработчик обновлятора).

Иммутабельность в C#

В разработке программного обеспечения иммутабельным (immutable — неизменяемым) называется объект, который после своего создания не может быть изменен. Зачем вам может понадобиться такой объект? Чтобы ответить на этот вопрос, давайте проведем анализ проблем, которые могут возникнуть в результате мутации (изменения) объектов. Вернемся к основам того, что делает каждое приложение: создает, извлекает, обновляет и удаляет данные (CRUD-операции). Ядро любого приложения манипулирует объектами. Ответ на вопрос о том, работает ли приложение в соответствии со своей спецификацией, в первую очередь определяется правильностью обработки данных. Вам необходимо быть уверенными, что код работает правильно, каждый раз, когда он затрагивает какой-либо объект.

Но что, если бы существовали объекты, которые не могли бы изменяться? Исходя из вышеизложенного, очевидно, что если объект не может измениться, то либо не будет кода, пытающегося изменить его, либо, если такой код все-таки написан, он не будет работать.

Если ваше приложение является многопоточным, иммутабельность должна быть фундаментальной частью вашей архитектуры, поскольку неизменяемые объекты по своей природе являются потокобезопасными и невосприимчивыми к состояниям гонки. Если ваше приложение использует data transport objects (DTO — объекты передачи данных), вам также следует серьезно подойти к иммутабельности ваших объектов. Предметом дискуссий остается лишь то, как наиболее эффективно реализовать и работать с иммутабельными объектами, поскольку C# не предлагает встроенной поддержки таких объектов. Эта статья предлагает ответ на этот вопрос.

Весь код, приведенный в статье, можно найти в это GitHub-репозитории.

Аргументы в пользу иммутабельности

Представьте себе любой произвольный объект. Это может быть пользователь, продукт или что-то совсем другое, что пришло вам в голову. Рассмотрим жизненный цикл этого объекта. Он создается, живет какое-то время и в какой-то момент прекращает свое существование. В основном вас интересует середина этого жизненного цикла, когда он живет в течение какого-то периода времени. Что, если в течении всего своего периода жизни (или даже его части) объект не должен изменится? Учитывая, что C# изначально не поддерживает абсолютную иммутабельность, как бы вы реализовали требование, согласно которому однажды созданный объект никогда не должен изменяться?

Если предпринимаются попытки изменения иммутабельного объекта, то возможны два сценария. Первый — компилятор отследит такие попытки, и приложение просто не соберется. Это самый лучший сценарий, потому что он представляет собой самое жесткое препятствие развертыванию такого приложения. Второй — если компилятор не может обнаружить все попытки изменения, то будут генерироваться исключения во время выполнения. Хотя исключения менее эффективны, чем обнаружение доступа на этапе компиляции, они тоже представляют из себя жесткое препятствие, говорящее нам о том, что произошла ошибка.

В современном программном обеспечении слишком часто происходят незаметные сбои. Другими словами, когда выполняется какая-нибудь недопустимая операция, она не обрабатывается в блоке try-catch и не результирует в наглядном исключении, как ей следовало бы. Чтобы работать корректно программное обеспечение должно следовать правилам. В нашем контексте правило заключается в том, что объекты не должны изменяться. Должен быть эффективный способ обеспечить как соблюдение этого правила, так и обратную связь при попытке нарушить это правило. Исключения являются подобным ограничением, которое дает нам такую ​​обратную связь.

Исключения являются ограничением, обеспечивающим обратную связь.

Если объект может изменяться, это открытая проблема, поскольку существует бесконечное количество способов, которыми объект может быть изменен. При работе с объектом, который не должен изменяться, но может, приложение неизбежно становится более сложным, поскольку должны быть встроены меры безопасности, гарантирующие, что этот объект никогда не изменится после своего создания. В C# есть несколько способов достижения иммутабельности, которые имеют свои ограничения.

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

Пример использования иммутабельности: DTO

DTO (Data Transport Object) — это классический пример, где мы хотели бы видеть иммутабельность. У DTO всего одно предназначение. Это предназначение, как следует из названия, заключается в передаче данных. Представьте, что вам приходится иметь дело с различными границами приложения (application boundaries), которые нужно пересекать вашим данным. Зачастую данные необходимо сериализовать, отправить в сервис на принимающей границе, а затем десериализовать, где затем происходит их гидратация в какой-нибудь объект (предположительно, этот объект также будет иммутабельным). В этом случае вы хотите гарантировать, что после создания объекта в течении всего его жизненного цикла ни при каких обстоятельствах никакие из его характеристик не будут изменены. По крайней мере, вы хотите быть уверены, что после установки свойств и блокировки объекта дальнейшие изменения будут невозможны.

Сложности c реализацией иммутабельности в C#

С реализацией иммутабельности в C# есть несколько сложностей. Чтобы проиллюстрировать их, в листинге 1 продемонстрирован самый прямой и простой способ добиться иммутабельности. Код работает, но есть две ощутимые проблемы. Во-первых, вы должны инициализировать значения свойств в конструкторе. Во-вторых, поскольку в деле замешаны приватные сеттеры, вы не можете использовать инициализатор свойств, который позволяет игнорировать свойства, которые вы не хотите устанавливать.

Листинг 1: Наименее масштабируемый, но самый простой способ добиться иммутабельности: через аргументы конструктора с неявными приватными сеттерами

public class SimpleImmutableClass < public SimpleImmutableClass(string firstName, string lastName) < FirstName = firstName; LastName = lastName; >//Неявные приватные сеттеры public string LastName < get; >public string FirstName < get; >>

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

Листинг 2: Иммутабельность, реализованная с помощью приватных сеттеров распространяется только на само определение свойства, но не на вложенные свойства и методы класса, который это свойство представляет

public class SimpleImmutableClass < public SimpleImmutableClass( string firstName, string lastName, Listitems ) < FirstName = firstName; LastName = lastName; Items = items; >//Неявные приватные сеттеры public string LastName < get; >public string FirstName < get; >public List Items < get; >> var sut = new SimpleImmutableClass( "John", "Petesen", new List() ); sut.Items.Add("BS"); sut.Items.Add("MBA"); sut.Items.Add("JD");

Тот факт, что вы можете изменить характеристики свойства, при том, что сеттер является приватным, может стать неожиданностью. Сеттер здесь влияет лишь на само свойство в отношении класса, содержащего его. Но никак не влияет на объект, который это свойство представляет. С примитивными типами (строки, целые числа, числа с плавающей запятой, логические значения и так далее) такой проблемы не возникает. Но только если они не заключены в какую-нибудь коллекцию, массив, словарь и так далее.

Как насчет структур — они же ведь иммутабельны, да?

Нет. Это простой ответ на этот вопрос. Это распространенное заблуждение, что структуры в C# по своей природе иммутабельны. Структуры должны быть иммутабельными. Структуры — отличный выбор, когда у вас есть небольшие объемы связанных данных. Свойства структуры могут быть примитивными или сложными. Вы можете использовать объектный инициализатор как в классах. Структуры более легковесны с точки зрения памяти, чем классы, и это их главное преимущество. Но я должен вам признаться, что не припомню, чтобы когда-либо использовал структуры в приложениях, несмотря на тот факт, что в некоторых случаях структура была бы лучшим техническим выбором. В листинге 3 показан пример простой изменяемой структуры.

Листинг 3: По умолчанию, несмотря на популярное заблуждение, структуры изменяемы, как и классы.

public struct Person < public string FirstName; public string LastName; public ListItems; > var sut = new Person() < FirstName = "John", LastName = "Petersen" >; sut.Items = new List(); sut.FirstName = "john"; sut.Items.Add("Structs are not immutable!");

Разница между обычным и корпоративным программным обеспечением заключается в том, как оно обрабатывает ситуации, когда что-то идет не так, и, в частности, в том, как оно сообщает о таких ситуациях.

Реализация иммутабельности в C# (почти без боли!)

Наши ожидания: во-первых, давайте согласимся с тем, что не все в C# можно сделать иммутабельным. Во фреймворке тысячи классов. Не все должно быть иммутабельным. Не каждый юзкейс требует иммутабельности. Во-вторых, от нас потребуется некоторое количество пользовательского кода. Полная иммутабельность, увы, не дана нам прямо из коробки. Поэтому нам придется немного пописать код. Хорошей новостью является то, что написать нужно действительно немного! В данном случае мы определим абстрактный класс, на основе которого мы будем создавать наши конкретные (concrete) классы. Эти конкретные классы должны будут следовать всего нескольким правилам.

Исходя из вышеизложенного, нам бы пригодились инициализаторы. Поэтому наши сеттеры должны быть публичными. Это означает, что нам нужно будет использовать какой-то механизм, чтобы сделать свойства с публичными сеттерами иммутабельными. Другими словами, нам нужно будет использовать пользовательский код!

Шаг 1: Использование JSON.NET и System.Immutable.Collections

На рисунке 1 показаны два пакета Nuget, необходимые для этого решения: NewtonSoft.Json (JSON.Net) и System.Immutable.Collections.

Рис. 1. Два пакета Nuget, необходимые для реализации иммутабельности.

JSON.NET требуется для упрощения сериализации и десериализации объектов. Несмотря на то, что иммутабельные объекты не изменяются, нам все-таки необходимо внести быстрые изменения в рамках создания нового объекта. JSON.NET — это средство, с помощью которого мы можем быстро создавать и управлять иммутабельными объектами.

System.Collections.Immutable — это пакет Nuget от Microsoft, который содержит иммутабельные версии списков, словарей, массивов, хешей и очередей. Это как раз те виды объектов, которые демонстрируют проблему с приватными сеттерами и свойствами коллекциями и массива. Если вы уже успели забыть, приватный сеттер не запрещает вам добавление или манипуляции с элементами во вложенной коллекции.

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

Шаг 2: Создание пользовательских исключений

Самое главное различие между обычным и “корпоративным” программным обеспечением заключается вовсе не в ключевом функционале. Разница заключается в том, как они обрабатывает ситуации, когда что-то идет не так, и, в частности, в том, как они сообщают о таких ситуациях. В нашем случае нам нужно отловить две недопустимые ситуации. Первая — когда делается попытка изменить иммутабельное свойство. Вторая — это попытка добавить к инстансу иммутабельного класса свойство, которое не поддерживает иммутабельность. В листинге 4 показано исключение ImmutableObjectEditAttempt .

Листинг 4: Исключение ImmutableObjectEditAttempt генерируется всякий раз, когда предпринимается попытка изменить иммутабельное свойство после того, как оно было установлено.

public class ImmutableObjectEditException : Exception < public ImmutableObjectEditException() : base("An immutable object cannot be changed after it has been created.") < >>

В листинге 5 показано исключение InvalidDataType .

Листинг 5: Исключение InvalidDataTypeException генерируется при попытке определить свойство с типом, не содержащимся в ValidImmutableClassTypes .

public class InvalidDataTypeException : Exception < public static ImmutableHashSetValidImmutableClassTypes = ImmutableHashSet.Create( "Boolean", "Byte", "SByte", "Char", "Decimal", "Double", "Single", "Int32", "UInt32", "Int64", "UInt64", "Int16", "UInt16", "String", "ImmutableArray", "ImmutableDictionary", "ImmutableList", "ImmutableHashSet", "ImmutableSortedDictionary", "ImmutableSortedSet", "ImmutableStack", "ImmutableQueue" ); public InvalidDataTypeException( ImmutableHashSet invalidProperties) : base( $"Properties of an instance of " + "ImmutableClass may only " + "contain the following types: Boolean, Byte, " + "SByte, Char, Decimal, Double, Single, " + "Int32, UInt32, Int64, " + "UInt64, Int16, UInt16, String, ImmutableArray, " + "ImmutableDictionary, ImmutableList, ImmutableQueue, " + "ImmutableSortedSet, ImmutableStack or ImmutableClass. " + $"Invalid property types: " + $" ") < Data.Add("InvalidPropertyTypes", invalidProperties.ToArray()); >>
Шаг 3: Создание иммутабельного абстрактного класса

В этом разделе мы сконцентрируемся на абстрактном классе ImmutableClass . В листинге 6 показан его конструктор.

Листинг 6: Конструктор ImmutableClass гарантирует, что в свойствах объекта присутствуют только допустимые типы данных. Если мы пытаемся установить туда недопустимый тип, генерируется исключение, что предотвращает создание объекта.

public abstract class ImmutableClass < private bool _lock; protected ImmutableClass() < var properties = GetType() .GetProperties() .Where(x =>x.PropertyType.BaseType.Name != "ImmutableClass") .Select(x => x.PropertyType.Name.Substring(0, (x.PropertyType.Name.Contains("`") ? x.PropertyType.Name.IndexOf("`", StringComparison.Ordinal) : x.PropertyType.Name.Length))) .ToImmutableHashSet(); var invalidProperties = properties .Except(InvalidDataTypeException .ValidImmutableClassTypes); if (invalidProperties.Count > 0) throw new InvalidDataTypeException(invalidProperties); >

Когда создается инстанс иммутабельного объекта, в приватное поле _lock устанавливается значение true . Одно из требований — сделать инициализацию свойств доступной. Если вы хотите использовать конструктор для инициализации свойств инстанса вашего иммутабельного класса, у вас есть такая возможность. Использование инициализаторов предполагает, что сеттеры свойств должны быть публичными. Раз я заговорил о сеттерах свойств, давайте посмотрим на листинг 7, где показан стандартный код сеттера для свойства. Если объект залочен ( _lock ), генерируется исключение ImmutableOjbectEditException (ранее показанное в листинге 4).

Листинг 7: С помощью стандартного сеттера вы можете сделать так, что для каждого свойства будет генерироваться исключение, если будет предпринята попытка изменить его после его установки.

protected void Setter(string name, T value, ref T variable)

Как вы уже могли догадаться, задумка заключается в том, что когда вы создаете инстанс класса на основе ImmutableClass и не установили какое-либо из его свойств при создании, это должно означать, что вы не собирались устанавливать это свойство. В листинге 8 показан общий статический метод Create для создания иммутабельного объекта.

Листинг 8: Create() принимает json-строку в качестве аргумента, чтобы создать залоченный инстанс ImmutableClass .

public static T Create() where T : ImmutableClass < ImmutableClass retVal = Activator.CreateInstance(); retVal._lock = true; return (T)retVal; >

В следующем разделе будут приведены примеры, иллюстрирующие работу с ImmutableClass . Метод Create() автоматически лочит возвращаемый инстанс.

Шаг 4: Реализация ImmutableClass

В листинге 9 показаны два подхода к реализации инстанса ImmutableClass . Здесь представлен инстанс класса Person, наследующего абстрактный класс ImmutableClass . В первом подходе происходит гидратация иммутабельного объекта JSON-строкой. Как создавать JSON оставлю решать вам. Если имена полей подходят, JSON.NET с помощью функции десериализации правильно гидратирует объект. Второй подход заключается в создании класса Person напрямую, вне метода Create. Примечание: когда вы создаете инстанс напрямую, он НЕ ЯВЛЯЕТСЯ иммутабельным, поскольку для поля _lock не установлено значение true .

Листинг 9: Иммутабельный инстанс можно создать либо с помощью JSON-строки, либо с помощью изменяемого инстанса иммутабельного класса.

public class Person : ImmutableClass < private string _firstName; public string FirstName < get =>_firstName; set => Setter( MethodBase .GetCurrentMethod() .Name .Substring(4), value, ref _firstName); > private string _lastName; public string LastName < get =>_lastName; set => Setter( MethodBase .GetCurrentMethod() .Name .Substring(4), value, ref _lastName); > > var immutablePerson = ImmutableClass.Create ( "" ); //Или immutablePerson = ImmutableClass.Create ( new Person() < FirstName = "John", LastName = "Petersen">); //Следующая строка сгенерирует исключение immutablePerson.FirstName = "john"; >

На рисунке 2 показано выброшенное исключение, возникающее при попытке изменить свойство инстанса иммутабельного класса.

Рисунок 2: При попытке изменить неизменяемое свойство инстанса класса возникает исключение ImmutableObjectEditException.

Свойства класса, основанного на ImmutableClass , могут быть следующих типов:

  • Boolean
  • Byte
  • SByte
  • Char
  • Decimal
  • Double
  • Single
  • Int32
  • UInt32
  • Int64
  • UInt64
  • Int16
  • UInt16
  • String
  • ImmutableArray
  • ImmutableDictionary
  • ImmutableList
  • ImmutableHashSet
  • ImmutableSortedDictionary
  • ImmutableQueue
  • ImmutableSortedSet
  • ImmutableStack
  • ImmutableClass

Первые 14 являются стандартными примитивными типами .NET. Следующие шесть, ImmutableArray, ImmutableDictionary, ImmutableList, ImmutableHashSet, ImmutableSortedDictionary и ImmutableQueue, реализуются пакетом System.Collections.Immutable Nuget . Этот Nuget пакет также предоставляет методы расширения для их базовых типов .NET, что упрощают приведение к иммутабельным аналогам. Наконец, классы на основе ImmutableClass могут содержать в себе другие классы на основе ImmutableClass . Больше информации о System.Collections.Immutable можно найти здесь.

В листинге 10 показана немного более сложная версия класса Person .

Листинг 10: Иммутабельный класс может содержать свойства с инстансами других иммутабельных классов.

public class Person : ImmutableClass < private string _firstName; public string FirstName < get =>_firstName; set => Setter( MethodBase .GetCurrentMethod() .Name .Substring(4), value, ref _firstName); > private string _lastName; public string LastName < get =>_lastName; set => Setter( MethodBase .GetCurrentMethod() .Name .Substring(4), value, ref _lastName); > private ImmutableArray _schools; public ImmutableArray Schools < get =>_schools; set => Setter( MethodBase .GetCurrentMethod() .Name .Substring(4), value, ref _schools); > >

Свойство ImmutableArray решает проблему со свойствами на основе коллекций и массивов. System.Collections.Immutable содержит иммутабельные аналоги всех базовых типов коллекций в .NET. Если вы используете .NET Core или Standard, System.Collections.Immutable также поддерживает эти платформы.

Заключение

Если вы будете искать в интернете, как добиться иммутабельности в C#, вы заметите разные вариации одной темы. Эти подходы, как правило, используют параметры конструктора для инициализации, делая резервную переменную доступной только для чтения и удаляя сеттеры (оставляя только геттеры) для публичных свойств. Если в вашем классе есть только примитивные типы, этот подход будет работать. Если в вашем классе много свойств примитивного типа, этот подход будет работать, но менее осуществим. Используя этот подход, вы не можете использовать инициализаторы свойств. Поскольку DTO являются распространенным вариантом использования иммутабельности и поскольку DTO часто приходится сериализовать и десериализовать, отсутствие сеттеров сильно удручает.

Чтобы обойти эти ограничения, разработчики часто обращаются к написанию своего кода для решения проблемы. Хотя и какой-никакой код необходим для заполнения пробелов в том, что не входит в стандартную комплектацию, чем больше кода добавляется к смеси, тем больше проблем возникает. Чтобы убедиться, что объекты, которые должны быть иммутабельными, иммутабельны, обязательно используйте модульное тестирование. Представленный здесь подход подчеркивал простоту с, насколько это возможно, минимальным количеством кода. Убедившись, что необходимые объекты являются иммутабельными, вы можете быть уверены, что эти объекты не будут подвержены побочным эффектам остального кода. В результате получаются более простые приложения, требующие меньшего тестирования.

Сегодня вечером состоится бесплатное занятие «Базы данных: реляционные базы и работа с ними», на котором мы рассмотрим несколько способов работы с реляционными базами данных в приложениях .NET на примере PostgreSQL + ADO.NET и ORM (Dapper, Entity Framework). Все желающие могут зарегистрироваться по ссылке.

  • c#
  • иммутабельность
  • реляционные бд
  • PostgreSQL + ADO.NET

Объект и ЭтотОбъект

Здравствуйте! Читаю.. другой раз путаешься из за двух схожих слов, которые в отладчике выдают практически одно и то же.
Понятно что в модуле объекта мы обращаемся к реквизитам по имени, в модуле формы мы обращаемся к реквизитам данного объекта через Объект.
А «ЭтотОбъект» мы используем только для получения формы или в каких ещё случаях можно манипулировать им?

По теме из базы знаний

  • Внеконтекстный вызов методов объекта обработки (отчета) в управляемой форме
  • Как сделать OneScript объектно-ориентированным
  • Защита объектов от изменения обменом
  • Заполнение объектов в модели MVC
  • Шаблоны новых объектов 1С для 1С:Бухгалтерии предприятия
  • Дата
  • Дата
  • Рейтинг всех уровней
  • Рейтинг 1-го уровня
  • Древо развёрнутое
  • Древо свернутое

Свернуть все
2. Rothschild 30.01.14 14:24 Сейчас в теме
(1) Bukaska, привет!
Опять ты смотрю все перепутала и опять тебя надо спасать.
;))))
3. Rothschild 30.01.14 14:28 Сейчас в теме

ДокументОбъект. (DocumentObject.)
ЭтотОбъект (ThisObject)
Использование:
Только чтение.
Описание:
Тип: ДокументОбъект. Содержит сам объект базы данных. Предназначено, в основном, для получения данного объекта встроенного языка в модуле объекта или модуле формы.

ЭтотОбъект — имя для обращения к текущему объекту в модуле объекта.
В ООП есть аналог — свойство THIS

Иногда в формах объектов (обычных) «основной реквизит» обзывают «Объект»
тогда это фактически одно и тоже Объект == ЭтотОбъект

Атрибут ЭтотОбъект —
— в обычных формах доступен в силу расширения формы, связанного с «основным реквизитом»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *