diff --git a/docs/checks/code_typification.md b/docs/checks/code_typification.md new file mode 100644 index 00000000..6f670292 --- /dev/null +++ b/docs/checks/code_typification.md @@ -0,0 +1,1132 @@ +# Типизация кода + +> Методическая рекомендация + +[TOC text bullet hierarchy] + +## Цель + +Снизить количество ошибок, выявляемых на этапе разработки, и исключить практику написания кода в режиме отладки за счет максимально возможной типизации кода для статического анализатора в 1C:EDT. + +Кроме того, типизация кода позволит активнее применять различные средства автоматической обработки кода (автоформатирование, рефакторинг, автоперевод кода и так далее). + +Следует использовать следующие возможности: +1. Динамический расчет типов, выполняемый в 1С:EDT на основе контекста 1С:Предприятия и метаданных конфигурации. +2. Статическое указание типов в документирующих/типизирующих комментариях, ссылки на функции-конструкторы и ссылки на входящие параметры. +3. Включение "строгой типизации" для модулей. + +## Включение строгой типизации + +При использовании дополнительного расширения "1С:Стандарты разработки" для 1С:EDT рекомендуется для модуля включить строгую типизацию, которая будет контролировать, что все типы были созданы правильно, во всех местах используется жесткие ссылки на объекты-создатели, что нет смены типа у переменной. + +Контроль типизации будет выполняться для всего модуля, включая не экспортные методы. Контролируется наличие типов всех переменных, используемых вызовов общих модулей, при обращении к полям/свойствам объектов контролируется их наличие и их типы. + +Для этого необходимо в заголовке модуля указать аннотацию до первого семантического объекта (области, процедуры, переменной): + +```bsl +//@strict-types + +#Область ПрограммныйИнтерфейс +... +``` + +Далее 1C:EDT будет отображать ошибки, если с объектами и их типами что-то не корректное. + +#### Применение строгой типизации + +* Для всех новых конфигураций, не имеющих большого наследия не типизированного кода, для всех модулей следует включать строгую типизацию. + +* Для существующих модулей рекомендуется сначала выполнить адаптацию кода и после включить строгую типизацию. + +## Использование контекстного помощника ввода + +1. Для того, чтобы код был максимально типизирован изначально - при написании кода следует использовать контент-ассист (или контекстный помощник ввода) Ctrl+Space, который подсказывает все известные свойства объекта. +2. При этом, если помощник ввода не подсказывает нужные свойства - следует сначала уточнить типы (исправить типы) для текущего объекта и продолжить набор текста по известным свойствам. +3. Использование типизации кода снижает потребность написания кода в режиме отладки с целью определения типов переменных, т.к. типы переменных подсказывает среда разработки. +4. Для проверки, того, что объект имеет статический или динамический тип - необходимо выделить нужный объект и нажать `F2` для отображения описания объекта - перед именем объекта должен отображаться его тип. + + +## Инициализация локальных переменных + +- Запрещается инициализация переменных через `Перем` т.к. такая переменная инициализируется с типом `Неопределенно` и дальнейшая смена типа значения может быть не видна для статического анализатора + +НЕПРАВИЛЬНО: + +```bsl +Процедура ОбработкаТекущейСтроки(ТЧ) + Перем ТекущаяСсылка; + + Для каждого СтокаТЧ из ТЧ Цикл + ТекущаяСсылка = СтрокаТЧ.Ссылка; + ... + КонецЦикла; + Объект = ТекущаяСсылка.ПолучитьОбъект(); +... +``` + +ПРАВИЛЬНО: + +```bsl +Процедура ОбработкаТекущейСтроки(ТЧ) + ТекущаяСсылка = Справочники.Номенклатура.ПустаяСсылка(); + + Для каждого СтокаТЧ из ТЧ Цикл + ТекущаяСсылка = СтрокаТЧ.Ссылка; + ... + КонецЦикла; + Объект = ТекущаяСсылка.ПолучитьОбъект(); +... +``` + +НЕПРАВИЛЬНО: + +```bsl +... +Перем ИдентификаторСтрокиПравилаВыгрузки; +... +Если НЕ ТелоСообщения.Свойство("ИдентификаторСтрокиПравилаВыгрузки", ИдентификаторСтрокиПравилаВыгрузки) Тогда + ИдентификаторСтрокиПравилаВыгрузки = СловарьМС.ПустойИдентификатор(); +КонецЕсли; +``` + +ПРАВИЛЬНО: + +```bsl +... +ИдентификаторСтрокиПравилаВыгрузки = СловарьМС.ПустойИдентификатор(); + +Если ТелоСообщения.Свойство("ИдентификаторСтрокиПравилаВыгрузки") Тогда + ИдентификаторСтрокиПравилаВыгрузки = ТелоСообщения.ИдентификаторСтрокиПравилаВыгрузки; +КонецЕсли; +``` + +- Запрещается инициализировать переменные внутри циклов или условий и последующим использованием их вне циклов/условий - т.к. 1С:Предприятие создает все локальные переменные сразу при входе в процедуру - то статическому анализатору невозможно отследить, где была создана переменная и с каким типом +- Уточнение типа локальной переменной, инициализированной, например, функцией возвращающей более общий тип, возможно через указание типа в строке: + +НЕПРАВИЛЬНО: + +```bsl +// Создает новый объект Номенклатуры по переданному типу +МойОбъект = ОбщийМодуль.СоздатьНовыйОбъектПоТипу(ТипОбъекта); +``` + +ПРАВИЛЬНО: + +```bsl +МойОбъект = ОбщийМодуль.СоздатьНовыйОбъектПоТипу(ТипОбъекта); // СправочникОбъект.Номенклатура - +``` + +Текущей функциональностью 1C:EDT является запись типа в одну строку в комментарии - что исключает местное описание сложных объектов данных (ТЗ, Структура, массив из структур) - для них необходимо использовать ссылки на функции конструкторы этих объектов. + +ПРАВИЛЬНО: + +```bsl +МойОбъект = Параметры.МойОбъект; // см. НовыйОбъектДанных +``` + +Допускается применение типизатора созданного через `Новый ОписаниеТипов(...)` для формирования переменных с необходимым типом начального пустого значения. + +ПРАВИЛЬНО: + +```bsl +Типизатор = Новый ОписаниеТипов("СправочникСсылка.Номенклатура,СправочникСсылка.НоменклатураПоставщика"); +МояПеременная = Типизатор.ПривестиЗначение(Неопределено); +``` + +## Инициализация переменных модуля + +Переменные модуля объекта, формы, конфигурации, включая глобальные переменные (экспортные), следует объявлять со статическим указанием типа в комментарии. Также следует инициализировать переменную модуля начальным/пустым значением в коде модуля (после всех процедур и функций). Для модуля формы допускается инициализация начального/пустого значения в обработчиках событий `ПриСозданииНаСервере` для серверных переменных и в обработчике `ПриОткрытии` для клиентских переменных. + +Исключением могут быть не экспортные переменные модуля, используемые только внутри общих механизмов и не имеющие обращений внутри текущего модуля объекта или формы. + +НЕПРАВИЛЬНО: + +```bsl +#Область ОписаниеПеременных + +&НаКлиенте +Перем КэшированныеЗначения; //используется механизмом обработки изменения реквизитов ТЧ + +&НаКлиенте +Перем ТекущиеДанныеИдентификатор; //используется для передачи текущей строки в обработчик ожидания + +&НаКлиенте +Перем ПараметрыДляЗаписи Экспорт; + +#КонецОбласти +``` + +ПРАВИЛЬНО: + +```bsl +#Область ОписаниеПеременных + +//используется механизмом обработки изменения реквизитов ТЧ +&НаКлиенте +Перем КэшированныеЗначения; // см. ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруКэшируемыеЗначения + +//используется для передачи текущей строки в обработчик ожидания +&НаКлиенте +Перем ТекущиеДанныеИдентификатор; // Число - + +&НаКлиенте +Перем ПараметрыДляЗаписи Экспорт; // Структура - + +#КонецОбласти + +.... + +ТекущиеДанныеИдентификатор = -1; +ПараметрыДляЗаписи = Новый Структура; +``` + + +## Сокращение типа локальной переменной или параметра + +- Можно безопасно сократить (или фактически установить для статического анализатора) тип локальной переменной метода, входящего параметра или переменной модуля (объекта, формы) метода через проверку типа: + +```bsl +Если ТипЗнч(МояПеременная) = Тип("СправочникОбъект.Товары") Тогда + МояПеременная.Артикул = ""; + МояПеременная.Записать(); +``` + +при этом внутри условия переменная будет указанного в проверке типа, как в рантайме, так и для статического анализатора. Условие проверки должно быть простым. + +## Инициализация ключей структуры + +- Значения ключей структуры (как в конструкторе, так и при добавлении в структуру) должны быть инициализированы сразу с пустым значением того типа, который будет использоваться в последствии. Динамическая типизация не позволяет рассчитать смену типа свойства структуры и, если не указывать пустое значение нужного типа, свойство будет инициализировано с типом Неопределенно. +- Смена типа значения ключа структуры - не допускается. + +НЕПРАВИЛЬНО: + +```bsl +Процедура Обработка() + + СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент(); +... + + Параметры = Новый Структура("Ссылка"); + Параметры.Ссылка = СправочникОбъект.Ссылка; +``` + +ПРАВИЛЬНО: + +```bsl +Процедура Обработка() + + СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент(); +... + + Параметры = Новый Структура("Ссылка", Справочники.Номенклатура.ПустаяСсылка()); + Параметры.Ссылка = СправочникОбъект.Ссылка; + +``` + +ПРАВИЛЬНО: + +```bsl +Процедура Обработка() + + СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент(); +... + + Параметры = Новый Структура; + Параметры.Вставить("Ссылка", СправочникОбъект.Ссылка); + +``` + +- При добавлении ключа в существующую структуру следует писать код "прозрачно" для статического анализатора, таким образом чтобы вставка ключа и его использование были в одной области видимости. + + +НЕПРАВИЛЬНО: + +```bsl +Процедура Обработка() + + Параметры = Новый Структура; + Если НеобходимоЗаполнить Тогда + ОбщийМодуль.ЗаполнитьПараметры(Параметры); + КонецЕсли; +... + + Если Параметры.МоеСвойство = ... +... +``` + +ПРАВИЛЬНО: + +```bsl +Процедура Обработка() + + Параметры = Новый Структура; + Параметры.Вставить("МоеСвойство", "Новое значение"); +... + + Если Параметры.МоеСвойство = ... +... +``` + +- Допускается использовать безопасный доступ к полю структуры в случае, когда структура чужая по отношению к текущему коду и описать поля и их типы нет возможности. Если необходимо безопасно проверить наличие ключа, если ключ опционален. Такой код является кандидатом для рефакторинга. + +```bsl +Процедура Обработка() + + Если Структура.Свойство("МоеСвойство") И ЗначениеЗаполнено(Структура.МоеСвойство) Тогда + МоеСвойство = Структура.МоеСвойство; // СправочникСсылка.Номенклатура - явное указание типа для неизвестного поля структуры +... +``` + +Функциональность будет реализована в 1C:EDT будущих версий. + +- Ограничение на получение значение ключа через метод "Свойство" не следует использовать: выглядит неплохо, но не анализируемая типизация + +НЕПРАВИЛЬНО: + +```bsl +Процедура Обработка() + Перем МояПеременная; + + Если Структура.Свойство("МоеСвойство", МояПеременная) И МояПеременная = "Полученное значение" Тогда +... +``` + +ПРАВИЛЬНО: + +```bsl +Процедура Обработка() + МояПеременная = ""; + + Если Структура.Свойство("МоеСвойство", МояПеременная) И МояПеременная = "Полученное значение" Тогда +... +``` + + +## Описание массивов + +- В общем случае - запрещено создавать массив `Новый Массив;` без указания типа его значений, если далее в коде текущего модуля происходит обращение к элементам массива. + +НЕПРАВИЛЬНО: + +```bsl + СписокСсылок = Новый Массив; + СписокСсылок.Добавить(Ссылка); +... + Для каждого Ссылка из СписокСсылок Цикл + Объект = Ссылка.ПолучитьОбъект(); +... +``` + +ПРАВИЛЬНО: + +```bsl + СписокСсылок = Новый Массив; // Массив из СправочникСсылка.Номенклатура - + СписокСсылок.Добавить(Ссылка); +... + Для каждого Ссылка из СписокСсылок Цикл + Объект = Ссылка.ПолучитьОбъект(); +... +``` +ПРАВИЛЬНО: + +```bsl + СписокСсылок = НовыйСписокНоменклатуры(); + СписокСсылок.Добавить(Ссылка); +... + Для каждого Ссылка из СписокСсылок Цикл + Объект = Ссылка.ПолучитьОбъект(); + +... + +// Возвращаемое значение: +// Массив из СправочникСсылка.Номенклатура +Функция НовыйСписокНоменклатуры() + Возврат Новый Массив; +КонецФункции +``` + +- Не рекомендуется использовать в качестве значений объекты разных типов: строки с числами, простые типы со ссылочными, объекты БД и структуры и т.д. +- Если значение массива сложное, рекомендуется использовать функцию-конструктор для инициализации пустого массива или массива с данными +- Если в функции создается массив и наполняется значениями, допустимо описывать возвращаемое значение типов элементов массива в описании функции, если в самой функции не происходит обращения к элементам массива. +- В документирующих комментариях следует указывать тип элементов массива для параметров и возвращаемых значений. + +## Описание таблицы значений, дерева значений + +- Следует при описании объекта `ТаблицаЗначений`, `ДеревоЗначений` всегда описывать все колонки и их типы. + +ПРАВИЛЬНО: + +```bsl +// Возвращаемое значение: +// ТаблицаЗначений: +// * Номенклатура - СправочникСсылка.Номенклатура +// * Характеристика - СправочникСсылка.ХарактеристикиНоменклатуры +// * Склад - СправочникСсылка.Склады +// * Количество - Число +// ... +Функция ТоварыДляСписания() +.... + Возврат Таблица; +КонецФункции +``` + +- В общем случае запрещается использовать описание типа `ТаблицаЗначений` в качестве входящего параметра экспортного метода, с описанием колонок. Правильно использовать ссылку на функцию-конструктор создающий эту таблицу. + +НЕПРАВИЛЬНО: + +```bsl +// Параметры: +// Таблица - ТаблицаЗначений +// * Номенклатура - СправочникСсылка.Номенклатура +Процедура ОбработкаТаблицы(Таблица) Экспорт + Для каждого СтрокаТаблицы из Таблица Цикл + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Таблица - см. ТоварыДляСписания +Процедура ОбработкаТаблицы(Таблица) + Для каждого СтрокаТаблицы из Таблица Цикл + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +- Исключением могут быть методы обрабатывающие произвольные таблицы, место создания которых неизвестно, но код метода рассчитывает на наличие определенных колонок. Все колонки, на которые рассчитывает код, с их типами должны быть указаны в документирующем описании. + +НЕПРАВИЛЬНО: + +```bsl +// Параметры: +// Таблица - ТаблицаЗначений +Процедура ОбработкаТаблицы(Таблица) Экспорт + Для каждого СтрокаТаблицы из Таблица Цикл + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Таблица - ТаблицаЗначений - произвольная таблица с товаром: +// * Номенклатура - СправочникСсылка.Номенклатура +Процедура ОбработкаТаблицы(Таблица) Экспорт + Для каждого СтрокаТаблицы из Таблица Цикл + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +- Исключением являются методы обрабатывающие таблицу значений с неопределенным набором колонок, например "копирование таблицы в структуру", при этом код не обращается к колонкам напрямую `СтрокаТЗ.Номенклатура = ...` а использует для обращения список колонок, сформированный в рантайме `СтрокаТЗ[ИмяКолонки] = ...`. + +## Описание строки таблицы или дерева значений + +Следует использовать один из двух вариантов: +- прямое указание колонок, на которые рассчитывает код в данной процедуре + +НЕПРАВИЛЬНО: + +```bsl +// Параметры: +// СтрокаТаблицы - СтрокаТаблицыЗначений +Процедура ОбработкаТаблицы(СтрокаТаблицы) + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// СтрокаТаблицы - СтрокаТаблицыЗначений: +// * Номенклатура - СправочникСсылка.Номенклатура +Процедура ОбработкаТаблицы(СтрокаТаблицы) + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... +``` + +- ссылка на строку ТЗ из функции-конструктора таблицы значений: + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// СтрокаТаблицы - СтрокаТаблицыЗначений: См. НоваяТаблицаСНоменклатурой +Процедура ОбработкаТаблицы(СтрокаТаблицы) + СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка(); +.... + +// Возвращаемое значение: +// ТаблицаЗначений - с колонками: +// * Номенклатура - СправочникСсылка.Номенклатура - +Функция НоваяТаблицаСНоменклатурой() +.... +``` + +## Описание соответствия + +- Соответствие - это фактически сложный тип с фиксированным набором колонок (ключ и значение) или аналогичен массиву из структур с 2 колонками. При этом описание типов у ключа и значения есть по умолчанию - `Произвольный`, и необходимо создавать функцию конструктор которая инициализирует соответствие и описывает типы ключа и значения. +- Следует избегать комбинации [различных типов в ключе и в значении соответствия](#ограничения-составных-типов). + +ПРАВИЛЬНО: + +```bsl +// Возвращаемое значение: +// Соответствие из КлючИЗначение: +// * Ключ - Строка +// * Значение - Массив из ДокументОбъект +Функция НовоеСоответствие() + ... + +``` + +## Ссылка на Табличную часть объекта + + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Объект - См. Справочник.Товары.ЕдиницыИзмерения +Процедура ОбработкаОбъекта(Объект) +``` + +## Ссылка на Строку Табличной части объекта + +> Такая ссылка еще не поддерживается в 1C:EDT! + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Объект - СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения +Процедура ОбработкаОбъекта(Объект) +``` + +## Ссылка на тип реквизита объекта или реквизита Табличной части объекта + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Реквизит1 - См. Справочник.Товары.Артикул +Процедура ОбработкаОбъекта(Реквизит1) +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// РеквизитТЧ - См. Справочник.Товары.ЕдиницыИзмерения.Единица +Процедура ОбработкаОбъекта(РеквизитТЧ) +``` + +## Ссылка на Форму + +Ссылка на форму указывается по полному имени формы + +ПРАВИЛЬНО: + +```bsl +/ Параметры: +// Форма - См. Справочник.Товары.Форма.ФормаЭлемента +Процедура ОбработкаОбъекта(Форма) +``` + +## Ссылка на Элемент Формы, Реквизит формы + +Ссылка тип реквизита формы + +ПРАВИЛЬНО: + +```bsl +/ Параметры: +// Объект - См. Справочник.Товары.Форма.ФормаЭлемента.Объект +Процедура ОбработкаОбъекта(Объект) +``` + +Ссылка тип элемента формы + +ПРАВИЛЬНО: + +```bsl +/ Параметры: +// Список - См. Справочник.Товары.Форма.ФормаСписка.Элементы.Список +Процедура ОбработкаОбъекта(Список) +``` + +## Ссылка на Текущие данные динамического списка формы + +Использование ссылки на тип текущих данных динамического списка на форме не поддерживается в документирующих комментариях. +В место этого следует передавать сам элемент формы и далее обрабатывать `ТекущиеДанные` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Список - См. Справочник.Товары.Форма.ФормаСписка.Элементы.Список +Процедура ОбработкаОбъекта(Список) + ЗначениеАртикула = Список.ТекущиеДанные.Артикул; + ... +``` + +## Ссылка на Строку таблицы значений формы + +> Такая ссылка еще не поддерживается в 1C:EDT! + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// СтрокаТЗ - ДанныеФормыЭлементКоллекции: См. Справочник.Товары.Форма.ФормаСписка.Элементы.ТЗ +Процедура ОбработкаОбъекта(СтрокаТЗ) +``` + +## Ссылка на тип параметра метода в модели менджера, объекта, общем модуле + +Ссылка на тип параметра экспортного метода из модуля менеджера + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Список - См. Справочники.Товары.МетодМодуляМенеджера.Параметр1 +Процедура ОбработкаОбъекта(Список) +``` + +Ссылка на тип параметра экспортного метода из модуля объекта + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Список - См. СправочникОбъект.Товары.МетодМодуляОбъекта.Параметр2 +Процедура ОбработкаОбъекта(Список) +``` + +## Наследование типов параметров по сигнатуре метода + +При размещении процедур в общих модулях (обычно "Переопределяемый") из общих процедуры могут быть вызваны наследники какого-либо механизма библиотеки. + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Список - См. СправочникОбъект.Товары.МетодМодуляОбъекта.Параметр2 +// Реквизит - См. Справочник.Товары.ЕдиницыИзмерения.Единица +// Объект - СправочникОбъект.Товары +Процедура ОбработкаОбъекта(Список, Реквизит, Объект) Экспорт + Документы.РеализацияТоваров.ОбработкаОбъекта(Список, Реквизит, Объект); + Документы.ПоступлениеТоваров.ОбработкаОбъекта(Список, Реквизит, Объект); + ... +``` + +В модуле менеджера соответствующих объектов можно сослаться на сигнатуру параметров метода, чтобы не описывать полностью все типы. +Необходимо указать только ссылку на метод, других комментариев или элементов документирующих комментариев быть не должно. + +ПРАВИЛЬНО: + +```bsl +// См. ОбщийМодульПодсистема1Переопределяемый.ОбработкаОбъекта +Процедура ОбработкаОбъекта(Список, Реквизит, Объект) +``` + +В этом случае будет выполнено сопоставление имен параметров текущего метода с именами параметров метода "интерфейса". + +## Функции-конструкторы сложных объектов данных + +Для сложных типов, создаваемых на основе абстрактных платформенных типов (`Структура`, `Соответствие`, `ТаблицаЗначений`, `ДеревоЗначений` и др.), следует использовать функцию-конструктор данных. + +Наименование функции следует выбирать как `Новый`/`Новая`/`Новое` (`New`) и наименование объекта данных. + +ПРАВИЛЬНО: + +```bsl +// Возвращаемое значение: +// ТаблицаЗначений: +// * Номенклатура - СправочникСсылка.Номенклатура +Функция НоваяТаблицаОтобраннойНоменклатуры() + .... +КонецФункции +``` + +- Конструктор данных может так же выполнять функцию заполнения данных т.е. предоставлять новый объект с заполненными данными. Например, функция выполняет запрос к БД и возвращает таблицу значений с колонками и данными. + +### Указание ссылки на функцию-конструктор данных + +При описании параметра метода следует указывать ссылку на функцию-конструктор данных без указания исходного базового типа данных (структура, таблица значений и т.д.). + +НЕПРАВИЛЬНО 1: + +```bsl +// Заполняет список команд печати. +// +// Параметры: +// КомандыПечати - ТаблицаЗначений - состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати. +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +НЕПРАВИЛЬНО 2: + +```bsl +// Заполняет список команд печати. +// +// состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати. +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +НЕПРАВИЛЬНО 3: + +```bsl +// см. УправлениеПечатью.СоздатьКоллекциюКомандПечати. +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +НЕПРАВИЛЬНО 4: + +```bsl +// Заполняет список команд печати. +// +// Параметры: +// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати. +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +НЕПРАВИЛЬНО 5: + +```bsl +// Заполняет список команд печати. +// +// Параметры: +// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати - команды для заполнения +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +ПРАВИЛЬНО: + +```bsl +// Заполняет список команд печати. +// +// Параметры: +// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати +// +Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт +``` + +### Функции-получатели сложных объектов данных + +При обработке более общих типов объектов в общих модулях может потребоваться получить заранее известный комплексный тип (`Структура`, `ТаблицаЗначений`, `ДеревоЗначений`), при этом, функции-конструктора таких данных может не существовать, например когда данные создаются объектом метаданных, формой, СКД и так далее. Для таких случаев следует использовать функцию-получатель, описывающую тип возвращаемого значения со всеми необходимыми свойствами/полями объекта. +Для клиентских процедур не рекомендуется использовать конструкторы из модулей с контекстом 'Сервер', но без контекста 'Вызов сервера'. + +НЕПРАВИЛЬНО: + +```bsl +// Параметры: +/// Форма - ФормаКлиентскогоПриложения - форма, для элементов которых производится заполнение +// Параметры - Структура - дополнительные параметры дерева. +// +Процедура ОбновитьДеревоНовыхЭлементов(Форма, Параметры) Экспорт + + ИмяЭлементаДерева = Параметры.ИмяЭлементаДерева; + ... + ДеревоЭлементов = Форма.РеквизитФормыВЗначение(ИмяЭлементаДерева); + ДеревоЭлементов.Строки.Очистить(); + ... + СтрокаЭлемент = ДеревоЭлементов.Строки.Добавить(); + СтрокаЭлемент.Наименование = ...; +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +/// Форма - ФормаКлиентскогоПриложения - форма, для элементов которых производится заполнение +// Параметры - Структура - дополнительные параметры дерева. +// +Процедура ОбновитьДеревоНовыхЭлементов(Форма, Параметры) Экспорт + + ИмяЭлементаДерева = Параметры.ИмяЭлементаДерева; + ... + ДеревоЭлементов = ДеревоНовыхЭлементов(Форма, ИмяЭлементаДерева); + ДеревоЭлементов.Строки.Очистить(); + ... + СтрокаЭлемент = ДеревоЭлементов.Строки.Добавить(); + СтрокаЭлемент.Наименование = ...; + +КонецПроцедуры + +// Параметры: +// ... +// +// Возвращаемое значение: +// ДеревоЗначений: +// * Наименование - Строка +// * ВидЭлемента - ... +// * ... +Функция ДеревоНовыхЭлементов(Форма, ИмяЭлементаДерева) + Возврат Форма.РеквизитФормыВЗначение(ИмяЭлементаДерева); +КонецФункции +``` + +## Ограничение на использование в документирующих описаниях Массивов, ТЗ, Структур + +Для параметров экспортных методов рекомендуется указывать ссылку на функцию-конструктор таких объектов. + +Не рекомендуется указывать в качестве параметров не экспортных методов сложные типы на основе `Массивов`, `Таблиц Значений`, `Структур` и т.д., если объект является цельным, созданным в текущем модуле и контекст передачи объекта ограничен текущим модулем. В этом случае 1C:EDT не выполняет динамическую типизацию таких параметров, а использует указанные статические типы в описании метода. + +Допускается описание таких типов во входящих параметрах, если описывается часть объекта или программный интерфейс объекта, например несколько колонок `ТЧ.Товары` для которых выполняется расчет, при этом не существует одной единой функции-конструктора на которую можно сослаться в описании. + +## Описание программных интерфейсов для реализации в прикладных объектах + +Существует понятие "описания программного интерфейса объекта" и его реализация в прикладных объектах конфигурации. С применением библиотечного подхода к разработке конфигураций, библиотека может описывать некий программный интерфейс который должен быть реализован в прикладном объекте, для того чтобы библиотечный механизм имел доступ к объекту. + +Например, механизм "Свойства" из библиотеки БСП описывает интерфейс объекта: Наличие табличной части `ДополнительныеРеквизиты`, содержит колонки `Свойство`, `Значение`, `ТекстоваяСтрока` с определенными типами. Далее общий механизм библиотеки может обращаться к объекту например, при записи `УправлениеСвойствами.ПередЗаписьюНаСервере(ЭтотОбъект, ТекущийОбъект);` объекта из формы. + +Следует описывать интерфейс входящего объекта для той части объекта, которая требуется для работы механизма. При этом, следует учитывать, что механизм расширений свойств в документирующих коментариях поддерживается для тех типов, у которых возможны пользовательские совойства. Например: элементы коллекции вместо самих коллекций - тип `ТабличнаяЧасть` не имеет пользовательских свойств, а тип `СтрокаТабличнойЧасти` может иметь; тип `ДанныеФормыКоллекция` не имеет пользовательских свойств, а тип `ДанныеФормыЭлементКоллекции` может иметь. + + +НЕПРАВИЛЬНО: + +```bsl +// Параметры: +// Форма - ФормаКлиентскогоПриложения - уже настроена в процедуре ПриСозданииНаСервере: +// * Элементы - ВсеЭлементыФормы: +// ** ДополнительныеРеквизиты - ГруппаФормы +// * Свойства_ИспользоватьСвойства - Булево +// * Свойства_ИспользоватьДопРеквизиты - Булево +// .... +// +// Объект - Неопределено - взять объект из реквизита формы "Объект". +// - СправочникОбъект, ДокументОбъект, ДанныеФормыСтруктура - (по типу объекта) где: +// * ДополнительныеРеквизиты - ТабличнаяЧасть, ДанныеФормыКоллекция: +// ** Свойство - ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения - +// ** Значение - Характеристика.ДополнительныеРеквизитыИСведения - +// ** ТекстоваяСтрока - Строка - +Процедура ПеренестиЗначенияИзРеквизитовФормыВОбъект(Форма, Объект = Неопределено, ПередЗаписью = Ложь) Экспорт + + Если НЕ Форма.Свойства_ИспользоватьСвойства + ИЛИ НЕ Форма.Свойства_ИспользоватьДопРеквизиты Тогда + ... + + СтарыеЗначения = Объект.ДополнительныеРеквизиты.Выгрузить(); + Объект.ДополнительныеРеквизиты.Очистить(); + ... +``` + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Форма - ФормаКлиентскогоПриложения - уже настроена в процедуре ПриСозданииНаСервере: +// * Элементы - ВсеЭлементыФормы: +// ** ДополнительныеРеквизиты - ГруппаФормы +// * Свойства_ИспользоватьСвойства - Булево +// * Свойства_ИспользоватьДопРеквизиты - Булево +// .... +// +// Объект - Неопределено - взять объект из реквизита формы "Объект". +// - СправочникОбъект, ДокументОбъект, ДанныеФормыСтруктура - (по типу объекта) где: +// * ДополнительныеРеквизиты - ТабличнаяЧасть Из СтрокаТабличнойЧасти: +// ** Свойство - ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения - +// ** Значение - Характеристика.ДополнительныеРеквизитыИСведения - +// ** ТекстоваяСтрока - Строка - +Процедура ПеренестиЗначенияИзРеквизитовФормыВОбъект(Форма, Объект = Неопределено, ПередЗаписью = Ложь) Экспорт + + Если НЕ Форма.Свойства_ИспользоватьСвойства + ИЛИ НЕ Форма.Свойства_ИспользоватьДопРеквизиты Тогда + ... + + СтарыеЗначения = Объект.ДополнительныеРеквизиты.Выгрузить(); + Объект.ДополнительныеРеквизиты.Очистить(); + ... +``` + +## Описание используемых реквизитов и элементов форм для общего кода по работе с формами + +- Допускается описание части формы, общие для нескольких форм или реализация некого интерфейса для общего механизма конфигурации. Например, части одного общего механизма подключенные к множеству объектов конфигурации (дополнительные реквизиты, контактная информация и т.д.). + +ПРАВИЛЬНО: + +```bsl +// Параметры: +// Форма - ФормаКлиентскогоПриложения: +// * Объект - ДанныеФормыСтруктура, СправочникОбъект, ДокументОбъект - основной реквизит формы +// * Элементы - ВсеЭлементыФормы: +// ** Товары - ТаблицаФормы - элемент таблицы товаров +Процедура ПриСозданииНаСервере(Форма) + + Ссылка = Форма.Объект.Ссылка; + ТекущиеДанные = Форма.Элементы.Товары.ТекущиеДанные; +.... +``` + +- Если используется конкретная форма, следует в документирующих комментариях модулей (общих, менеджеров) указывать полную ссылку на форму. + +```bsl +// Параметры: +// Форма - см. Справочник.Номенклатура.Форма.ФормаЭлемента +Процедура ПриСозданииНаСервере(Форма) + + Ссылка = Форма.Объект.Ссылка; + Форма.Элементы.Артикул.Видимость = Истина; +.... +``` + +- Для функций общих модулей, рассчитывающих на дополнительные свойства и методы расширения типа `ФормаКлиентскогоПриложения` следует использовать соответствующие типы расширений управляемой формы: `РасширениеУправляемойФормыДляОбъектов`, `РасширениеУправляемойФормыДляДокумента`, `РасширениеУправляемойФормыДляДинамическогоСписка` и так далее. + +```bsl +// Параметры: +// Форма - РасширениеУправляемойФормыДляОбъектов - +Процедура ОбработкаЗакрытия(Форма) Экспорт + .... + + Форма.Записать(); + + .... +``` + +## Получение и открытие формы + +- При использовании методов  `ПолучитьФорму()` и `ОткрытьФорму()` с присвоением возвращаемого значения формы после открытия - следует указывать строковый литерал с полным именем формы первым параметром. +- Не следует выносить строковый литерал в отдельную переменную т.к. в этом случае переменная форма будет содержать общий тип `ФормаКлиентскогоПриложения` а не конкретный тип формы справочника номенклатуры, и весь контекст формы не будет доступен. В 1C:EDT на текущий момент поддерживается полная типизация только для полных имен форм, ссылки на основные формы для объекта метаданного `ФормаОбъекта`, `ФормаСписка` и т.д. возвращают общий тип `ФормаКлиентскогоПриложения`, полная типизация может быть поддержана в будущих версиях. + +НЕПРАВИЛЬНО: + +```bsl +ИмяФормы = "Справочник.Номенклатура.Форма.ФормаЭлемента"; +Форма = ПолучитьФорму(ИмяФормы); +``` + +ПРАВИЛЬНО: + +```bsl +Форма = ПолучитьФорму("Справочник.Номенклатура.Форма.ФормаЭлемента"); +// или +Форма = Справочники.Номенклатура.ПолучитьФорму("ФормаЭлемента"); +``` + +## Экспортные процедуры и функции + +- Все параметры методов и возвращаемые значения функций должны содержать описания типов, при этом пояснения к параметрам следует описывать в соответствии со стандартом [?] +- Локальные процедуры и функции не обязаны содержать описаний типов в документирующих комментариях и могут быть полностью расчетными, если код написан прозрачно для статического анализатора. + + +## Разрыв прямого контекста выполнения кода + +- В случае разрыва прямого вызова методов в рамках одного модуля (когда из одного метода напрямую вызывается другой метод) - следует описывать типы входящих параметров для не экспортных методов. + +## Использование временного хранилища и других контейнеров + +- Поместить во временное хранилище и после получить из него - можно все что угодно, поэтому необходимо выделять функцию возвращающую тип помещенный во временное хранилище использовать на нее ссылку при получении: + +```bsl +Данные = ПолучитьИзВременногоХранилища(Адрес); // см. НовыйОбъектДанных +``` + +- Аналогичный подход следует использовать при помещении пользовательского объекта внутрь другого объекта-контейнера, например `ДополнительныеПараметры` у объектов, или `Параметры` формы, пользовательские параметры элементов `СКД`, `ДополнительныеПараметры` у обработчиков оповещения и так далее. + +## Использование строковых литералов в качестве имен + +Не следует обращаться к элементу именованной коллекции, например `ВсеЭлементыФормы` и другие, через строковый индекс с именем элемента. Вместо этого следует обращаться напрямую к элементу так как в этом случае статический анализатор может контролировать наличие элемента, его тип. + +НЕПРАВИЛЬНО: + +```bsl +Элементы["Наименование"].Видимость = Ложь; +``` + +ПРАВИЛЬНО: + +```bsl +Элементы.Наименование.Видимость = Ложь; +``` + +Исключением может быть: + +* обращение к элементам и свойствам создаваемым программно и в момент статического анализа их еще не существует. При этом следует указывать тип локальной переменной полученного значения и уже потом обращаться к ее свойства: + +НЕПРАВИЛЬНО: + +```bsl +Элементы["Наименование"].Видимость = Ложь; +Элементы["Наименование"].Доступность = Истина; +``` + +ПРАВИЛЬНО: + +```bsl +Элемент = Элементы["Наименование"]; // ПолеФормы - +Элемент.Видимость = Ложь; +Элемент.Доступность = Истина; +``` + +* наличие таких элементов в коллекции может быть опциональным, при этом выполняется проверка наличия элемента. Например: исходный объект с неопределенными свойствами создается где-то вне и передается в текущую функцию входящим параметром. + +ПРАВИЛЬНО: + +```bsl + Элемент = Элементы.Найти("Наименование"); + Если Элемент <> Неопределено Тогда + Элемент.Видимость = Ложь; +... +``` + +ПРАВИЛЬНО: + +```bsl +Если Параметры.Свойство("Ссылка") Тогда + Ссылка = Параметры["Ссылка"]; // СправочникСсылка - +... +``` + + +## Ограничение на использование реквизитов формы с типом "Произвольный" + +Реквизит формы с типом `Произвольный` в коде следует рассматривать как "черный ящик". Статический анализатор не отслеживает изменение типа реквизита формы, поэтому весь обслуживающий этот реквизит код должен использовать проверку типа значения. + +В общем случае не следует использовать реквизит с типом `Произвольный` на форме, т.к. при этом разработчик самостоятельно несет ответственность за инициализацию значения в этом реквизите, за то, что все типы данных хранимые в реквизите могут быть сериализованы/десериализованы при передаче с клиента на сервер и обратно. Так же значение реквизита будет передаваться между клиентом и сервером всегда т.к. Платформа не контролирует модификацию значений, например внутри структуры, помещенной в произвольный реквизит. + +Реквизит с типом `Произвольный` следует заменять на честные реквизиты формы с определенными типами. Исключением может быть хранимые данные общих механизмов библиотек, которые через программный интерфейс инициализируют и обрабатывают хранимое в этом реквизите значение. + +Если заменить реквизит с типом `Произвольный` нет возможности, следует использовать функцию-конструктор для инициализации значения и дальнейших ссылок на типы. При этом не следует напрямую обращаться к реквизиту - для получения значения следует использовать функцию-получатель. + +ПРАВИЛЬНО: + +```bsl +// Возвращаемое значение: +// Структура: +// * Ссылка - СправочникСсылка.Номенклатура - ссылка на текущую номенклутру +// ... +Функция НовыйСложныйОбъектДанных() + Данные = Новый Структура; + Данные.Вставить("Ссылка", ...); + ... + Возврат Данные; +КонецФункции + +// Возвращаемое значение: +// см. НовыйСложныйОбъектДанных +Функция РеквизитПроизвольный() + Возврат РеквизитПроизвольный; +КонецФункции + +... +// Инициализация реквизита через функцию-конструктор +РеквизитПроизвольный = НовыйСложныйОбъектДанных(); + +// Обращение к значению в реквизите с произвольным типом +Ссылка = РеквизитПроизвольный().Ссылка; +``` + +## Использование параметров формы + +- Запрещается использовать параметр с типом "Произвольный" для передачи структуры параметров для инициализации формы. +- В редакторе формы на вкладке "Параметры" следует описывать все параметры, на которые опирается форма при открытии, включая необязательные. + +## Выборка и выгрузка из результата запроса + +При типизации выборки/выгрузки из результата запроса следует использовать возможности: + +1. Статическое описание всех полей выборки (полей таблицы значений выгрузки) в возвращаемом значении [функции-получателе данных](#функции-получатели-сложных-объектов-данных). Может применяться для функций, динамически формирующих текст запроса и выборку, а так же все экспортные функции, возвращающие выборку. +2. Динамическая типизация полей выборки на основе текста запроса. В будущих версиях 1C:EDT может быть реализована возможность обращаться к полям запроса, при условии что текст запроса и выборка (результат запроса) находятся в одной процедуре, текст запроса не содержит ошибок (открывается "Конструктором запросов"), выборка/выгрузка из результата запроса не передается в другой модуль для обработки. + +НЕПРАВИЛЬНО: + +```bsl +Запрос = Новый Запрос; +Запрос.Текст = + ТексЗапросаОстатков() + + ТекстЗапросаРезервов(); +РезультаЗапроса = Запрос.Выполнить(); +Выборка = РезультатЗапроса.Выбрать(); +Пока Выборка.Следующий() Цикл + Ссылка = Выборка.Ссылка.ПолучитьОбъект(); +... +``` + +ПРАВИЛЬНО: + +```bsl +// Возвращаемое значение: +// ВыборкаРезультатаЗапроса: +// * Номенклатура - СправочникСсылка.Номенклатура +Функция ОстаткиДляОбработки() + Запрос = Новый Запрос; + Запрос.Текст = + ТексЗапросаОстатков() + + ТекстЗапросаРезервов(); + РезультаЗапроса = Запрос.Выполнить(); + Возврат РезультатЗапроса.Выбрать(); +КонецФункции; + +... + +Выборка = ОстаткиДляОбработки(); +Пока Выборка.Следующий() Цикл + Ссылка = Выборка.Номенклатура.ПолучитьОбъект(); +... +``` + +ПРАВИЛЬНО: + +```bsl +Запрос = Новый Запрос; +Запрос.Текст = " + | ВЫБРАТЬ + | Номенклатура + |ИЗ + | РегистраНакопления.Остатки.Остатки + | + | ОБЪДИНИТЬ ВСЕ + | + |ВЫБРАТЬ + | Номенклатура + |ИЗ + | РегистрНакопления.Резервы.Остатки + |"; +РезультаЗапроса = Запрос.Выполнить(); +Выборка = РезультатЗапроса.Выбрать(); +Пока Выборка.Следующий() Цикл + Ссылка = Выборка.Номенклатура.ПолучитьОбъект(); +... +``` + +## Передача пользовательского объекта данных в другой модуль + +Объекты данных созданные пользовательским кодом и использованные вне текущего модуля - должны быть описаны функцией возвращающей значение. + +Использование типа созданного в текущем модуле, но не предназначенного для создания в другом модуле - следует использовать не экспортную функцию-конструктор, при этом существует 2 варианта: +1. Экспортная функция, которая возвращает это значение вместе с данными (функция-получатель) - в ней нужно в возвращаемом значении указать функцию-конструктор +2. Реализация какого-либо интерфейса из переопределяемого модуля - в этом случае, можно использовать ссылку на параметр + +## Описание типов макетов СКД и Табличного документа + +- В случае получения макета через универсальную процедуру - следует указывать в строке тип макета. + +```bsl +Макет = УправлениеПечатью.МакетПечатнойФормы("Документ.РеализацияТоваровУслуг.ПечатнаяФорма"); //см. Документ.РеализацияТоваровУслуг.Макет.ПечатнаяФорма +``` + +## Ограничение на создание пользовательских объектов-копий (Структуры, ТЗ) из платформенных объектов + +- Существуют процедуры которые создают ТЗ или структуру аналогичную какому-нибудь объект метаданных, далее выполняется копирование значений для каждого свойства +- Не рекомендуется, т.к. Необходимо будет описать весь объект самостоятельно +- В исключительных случаях (каких?) можно сделать функцию-конструктор описывающую смешанный тип `Структура + СправочникОбъект.Номенклатура` чтобы не описывать весь тип. При этом можно добавить в описание дополнительные колонки, которые вводятся для технических целей (помощь при копировании, передача дополнительной информации с объектом). +- Следует учитывать ограничение на описание строк ТЧ объектов метаданных. + +## Ограничения составных типов + +- В общем случае неправильно делать составные типы, которые мало похожи между собой. Например: + - Строка и СправочникОбъект + - Число и Массив + - Коллекции (массив, ТЗ, Соответствие, ТЧ, Структура) и простые типы (строка, число, ссылка) + - ДеревоЗначений и ТаблицаЗначений +- Наличие таких смешанных типов - однозначный повод для рефакторинга кода. diff --git a/docs/checks/readme.md b/docs/checks/readme.md index 194c0b33..76897cfc 100644 --- a/docs/checks/readme.md +++ b/docs/checks/readme.md @@ -7,4 +7,5 @@ - [Проверки прав ролей](right.md) - [Проверки модулей](bsl.md) - [Проверки языка запросов](ql.md) +- [Типизация кода](code_typification.md) - [АПК Ред.1 индекс соответствия](acc_index.md) diff --git a/docs/toc.xml b/docs/toc.xml index 7b28be29..339d9791 100644 --- a/docs/toc.xml +++ b/docs/toc.xml @@ -34,6 +34,8 @@ + +