Исправлен тип "ВыборкаРезультатаЗапроса" на "ВыборкаИзРезультатаЗапроса"
70 KiB
Типизация кода
Методическая рекомендация
[TOC text bullet hierarchy]
Цель
Снизить количество ошибок, выявляемых на этапе разработки, и исключить практику написания кода в режиме отладки за счет максимально возможной типизации кода для статического анализатора в 1C:EDT.
Кроме того, типизация кода позволит активнее применять различные средства автоматической обработки кода (автоформатирование, рефакторинг, автоперевод кода и так далее).
Следует использовать следующие возможности:
- Динамический расчет типов, выполняемый в 1С:EDT на основе контекста 1С:Предприятия и метаданных конфигурации.
- Статическое указание типов в документирующих/типизирующих комментариях, ссылки на функции-конструкторы и ссылки на входящие параметры.
- Включение "строгой типизации" для модулей.
Включение строгой типизации
При использовании дополнительного расширения "1С:Стандарты разработки" для 1С:EDT рекомендуется для модуля включить строгую типизацию, которая будет контролировать, что все типы были созданы правильно, во всех местах используется жесткие ссылки на объекты-создатели, что нет смены типа у переменной.
Контроль типизации будет выполняться для всего модуля, включая не экспортные методы. Контролируется наличие типов всех переменных, используемых вызовов общих модулей, при обращении к полям/свойствам объектов контролируется их наличие и их типы.
Для этого необходимо в заголовке модуля указать аннотацию до первого семантического объекта (области, процедуры, переменной):
//@strict-types
#Область ПрограммныйИнтерфейс
...
Далее 1C:EDT будет отображать ошибки, если с объектами и их типами что-то не корректное.
Применение строгой типизации
-
Для всех новых конфигураций, не имеющих большого наследия не типизированного кода, для всех модулей следует включать строгую типизацию.
-
Для существующих модулей рекомендуется сначала выполнить адаптацию кода и после включить строгую типизацию.
Использование контекстного помощника ввода
- Для того, чтобы код был максимально типизирован изначально - при написании кода следует использовать контент-ассист (или контекстный помощник ввода) Ctrl+Space, который подсказывает все известные свойства объекта.
- При этом, если помощник ввода не подсказывает нужные свойства - следует сначала уточнить типы (исправить типы) для текущего объекта и продолжить набор текста по известным свойствам.
- Использование типизации кода снижает потребность написания кода в режиме отладки с целью определения типов переменных, т.к. типы переменных подсказывает среда разработки.
- Для проверки, того, что объект имеет статический или динамический тип - необходимо выделить нужный объект и нажать
F2
для отображения описания объекта - перед именем объекта должен отображаться его тип.
Инициализация локальных переменных
- Запрещается инициализация переменных через
Перем
т.к. такая переменная инициализируется с типомНеопределенно
и дальнейшая смена типа значения может быть не видна для статического анализатора
НЕПРАВИЛЬНО:
Процедура ОбработкаТекущейСтроки(ТЧ)
Перем ТекущаяСсылка;
Для каждого СтокаТЧ из ТЧ Цикл
ТекущаяСсылка = СтрокаТЧ.Ссылка;
...
КонецЦикла;
Объект = ТекущаяСсылка.ПолучитьОбъект();
...
ПРАВИЛЬНО:
Процедура ОбработкаТекущейСтроки(ТЧ)
ТекущаяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Для каждого СтокаТЧ из ТЧ Цикл
ТекущаяСсылка = СтрокаТЧ.Ссылка;
...
КонецЦикла;
Объект = ТекущаяСсылка.ПолучитьОбъект();
...
НЕПРАВИЛЬНО:
...
Перем ИдентификаторСтрокиПравилаВыгрузки;
...
Если НЕ ТелоСообщения.Свойство("ИдентификаторСтрокиПравилаВыгрузки", ИдентификаторСтрокиПравилаВыгрузки) Тогда
ИдентификаторСтрокиПравилаВыгрузки = СловарьМС.ПустойИдентификатор();
КонецЕсли;
ПРАВИЛЬНО:
...
ИдентификаторСтрокиПравилаВыгрузки = СловарьМС.ПустойИдентификатор();
Если ТелоСообщения.Свойство("ИдентификаторСтрокиПравилаВыгрузки") Тогда
ИдентификаторСтрокиПравилаВыгрузки = ТелоСообщения.ИдентификаторСтрокиПравилаВыгрузки;
КонецЕсли;
- Запрещается инициализировать переменные внутри циклов или условий и последующим использованием их вне циклов/условий - т.к. 1С:Предприятие создает все локальные переменные сразу при входе в процедуру - то статическому анализатору невозможно отследить, где была создана переменная и с каким типом
- Уточнение типа локальной переменной, инициализированной, например, функцией возвращающей более общий тип, возможно через указание типа в строке:
НЕПРАВИЛЬНО:
// Создает новый объект Номенклатуры по переданному типу
МойОбъект = ОбщийМодуль.СоздатьНовыйОбъектПоТипу(ТипОбъекта);
ПРАВИЛЬНО:
МойОбъект = ОбщийМодуль.СоздатьНовыйОбъектПоТипу(ТипОбъекта); // СправочникОбъект.Номенклатура -
Текущей функциональностью 1C:EDT является запись типа в одну строку в комментарии - что исключает местное описание сложных объектов данных (ТЗ, Структура, массив из структур) - для них необходимо использовать ссылки на функции конструкторы этих объектов.
ПРАВИЛЬНО:
МойОбъект = Параметры.МойОбъект; // см. НовыйОбъектДанных
Допускается применение типизатора созданного через Новый ОписаниеТипов(...)
для формирования переменных с необходимым типом начального пустого значения.
ПРАВИЛЬНО:
Типизатор = Новый ОписаниеТипов("СправочникСсылка.Номенклатура,СправочникСсылка.НоменклатураПоставщика");
МояПеременная = Типизатор.ПривестиЗначение(Неопределено);
Инициализация переменных модуля
Переменные модуля объекта, формы, конфигурации, включая глобальные переменные (экспортные), следует объявлять со статическим указанием типа в комментарии. Также следует инициализировать переменную модуля начальным/пустым значением в коде модуля (после всех процедур и функций). Для модуля формы допускается инициализация начального/пустого значения в обработчиках событий ПриСозданииНаСервере
для серверных переменных и в обработчике ПриОткрытии
для клиентских переменных.
Исключением могут быть не экспортные переменные модуля, используемые только внутри общих механизмов и не имеющие обращений внутри текущего модуля объекта или формы.
НЕПРАВИЛЬНО:
#Область ОписаниеПеременных
&НаКлиенте
Перем КэшированныеЗначения; //используется механизмом обработки изменения реквизитов ТЧ
&НаКлиенте
Перем ТекущиеДанныеИдентификатор; //используется для передачи текущей строки в обработчик ожидания
&НаКлиенте
Перем ПараметрыДляЗаписи Экспорт;
#КонецОбласти
ПРАВИЛЬНО:
#Область ОписаниеПеременных
//используется механизмом обработки изменения реквизитов ТЧ
&НаКлиенте
Перем КэшированныеЗначения; // см. ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруКэшируемыеЗначения
//используется для передачи текущей строки в обработчик ожидания
&НаКлиенте
Перем ТекущиеДанныеИдентификатор; // Число -
&НаКлиенте
Перем ПараметрыДляЗаписи Экспорт; // Структура -
#КонецОбласти
....
ТекущиеДанныеИдентификатор = -1;
ПараметрыДляЗаписи = Новый Структура;
Сокращение типа локальной переменной или параметра
- Можно безопасно сократить (или фактически установить для статического анализатора) тип локальной переменной метода, входящего параметра или переменной модуля (объекта, формы) метода через проверку типа:
Если ТипЗнч(МояПеременная) = Тип("СправочникОбъект.Товары") Тогда
МояПеременная.Артикул = "";
МояПеременная.Записать();
при этом внутри условия переменная будет указанного в проверке типа, как в рантайме, так и для статического анализатора. Условие проверки должно быть простым.
Инициализация ключей структуры
- Значения ключей структуры (как в конструкторе, так и при добавлении в структуру) должны быть инициализированы сразу с пустым значением того типа, который будет использоваться в последствии. Динамическая типизация не позволяет рассчитать смену типа свойства структуры и, если не указывать пустое значение нужного типа, свойство будет инициализировано с типом Неопределенно.
- Смена типа значения ключа структуры - не допускается.
НЕПРАВИЛЬНО:
Процедура Обработка()
СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент();
...
Параметры = Новый Структура("Ссылка");
Параметры.Ссылка = СправочникОбъект.Ссылка;
ПРАВИЛЬНО:
Процедура Обработка()
СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент();
...
Параметры = Новый Структура("Ссылка", Справочники.Номенклатура.ПустаяСсылка());
Параметры.Ссылка = СправочникОбъект.Ссылка;
ПРАВИЛЬНО:
Процедура Обработка()
СправочникОбъект = Справочники.Номенклатура.СоздатьЭлемент();
...
Параметры = Новый Структура;
Параметры.Вставить("Ссылка", СправочникОбъект.Ссылка);
- При добавлении ключа в существующую структуру следует писать код "прозрачно" для статического анализатора, таким образом чтобы вставка ключа и его использование были в одной области видимости.
НЕПРАВИЛЬНО:
Процедура Обработка()
Параметры = Новый Структура;
Если НеобходимоЗаполнить Тогда
ОбщийМодуль.ЗаполнитьПараметры(Параметры);
КонецЕсли;
...
Если Параметры.МоеСвойство = ...
...
ПРАВИЛЬНО:
Процедура Обработка()
Параметры = Новый Структура;
Параметры.Вставить("МоеСвойство", "Новое значение");
...
Если Параметры.МоеСвойство = ...
...
- Допускается использовать безопасный доступ к полю структуры в случае, когда структура чужая по отношению к текущему коду и описать поля и их типы нет возможности. Если необходимо безопасно проверить наличие ключа, если ключ опционален. Такой код является кандидатом для рефакторинга.
Процедура Обработка()
Если Структура.Свойство("МоеСвойство") И ЗначениеЗаполнено(Структура.МоеСвойство) Тогда
МоеСвойство = Структура.МоеСвойство; // СправочникСсылка.Номенклатура - явное указание типа для неизвестного поля структуры
...
Функциональность будет реализована в 1C:EDT будущих версий.
- Ограничение на получение значение ключа через метод "Свойство" не следует использовать: выглядит неплохо, но не анализируемая типизация
НЕПРАВИЛЬНО:
Процедура Обработка()
Перем МояПеременная;
Если Структура.Свойство("МоеСвойство", МояПеременная) И МояПеременная = "Полученное значение" Тогда
...
ПРАВИЛЬНО:
Процедура Обработка()
МояПеременная = "";
Если Структура.Свойство("МоеСвойство", МояПеременная) И МояПеременная = "Полученное значение" Тогда
...
Описание массивов
- В общем случае - запрещено создавать массив
Новый Массив;
без указания типа его значений, если далее в коде текущего модуля происходит обращение к элементам массива.
НЕПРАВИЛЬНО:
СписокСсылок = Новый Массив;
СписокСсылок.Добавить(Ссылка);
...
Для каждого Ссылка из СписокСсылок Цикл
Объект = Ссылка.ПолучитьОбъект();
...
ПРАВИЛЬНО:
СписокСсылок = Новый Массив; // Массив из СправочникСсылка.Номенклатура -
СписокСсылок.Добавить(Ссылка);
...
Для каждого Ссылка из СписокСсылок Цикл
Объект = Ссылка.ПолучитьОбъект();
...
ПРАВИЛЬНО:
СписокСсылок = НовыйСписокНоменклатуры();
СписокСсылок.Добавить(Ссылка);
...
Для каждого Ссылка из СписокСсылок Цикл
Объект = Ссылка.ПолучитьОбъект();
...
// Возвращаемое значение:
// Массив из СправочникСсылка.Номенклатура
Функция НовыйСписокНоменклатуры()
Возврат Новый Массив;
КонецФункции
- Не рекомендуется использовать в качестве значений объекты разных типов: строки с числами, простые типы со ссылочными, объекты БД и структуры и т.д.
- Если значение массива сложное, рекомендуется использовать функцию-конструктор для инициализации пустого массива или массива с данными
- Если в функции создается массив и наполняется значениями, допустимо описывать возвращаемое значение типов элементов массива в описании функции, если в самой функции не происходит обращения к элементам массива.
- В документирующих комментариях следует указывать тип элементов массива для параметров и возвращаемых значений.
Описание таблицы значений, дерева значений
- Следует при описании объекта
ТаблицаЗначений
,ДеревоЗначений
всегда описывать все колонки и их типы.
ПРАВИЛЬНО:
// Возвращаемое значение:
// ТаблицаЗначений:
// * Номенклатура - СправочникСсылка.Номенклатура
// * Характеристика - СправочникСсылка.ХарактеристикиНоменклатуры
// * Склад - СправочникСсылка.Склады
// * Количество - Число
// ...
Функция ТоварыДляСписания()
....
Возврат Таблица;
КонецФункции
- В общем случае запрещается использовать описание типа
ТаблицаЗначений
в качестве входящего параметра экспортного метода, с описанием колонок. Правильно использовать ссылку на функцию-конструктор создающий эту таблицу.
НЕПРАВИЛЬНО:
// Параметры:
// Таблица - ТаблицаЗначений
// * Номенклатура - СправочникСсылка.Номенклатура
Процедура ОбработкаТаблицы(Таблица) Экспорт
Для каждого СтрокаТаблицы из Таблица Цикл
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
ПРАВИЛЬНО:
// Параметры:
// Таблица - см. ТоварыДляСписания
Процедура ОбработкаТаблицы(Таблица)
Для каждого СтрокаТаблицы из Таблица Цикл
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
- Исключением могут быть методы обрабатывающие произвольные таблицы, место создания которых неизвестно, но код метода рассчитывает на наличие определенных колонок. Все колонки, на которые рассчитывает код, с их типами должны быть указаны в документирующем описании.
НЕПРАВИЛЬНО:
// Параметры:
// Таблица - ТаблицаЗначений
Процедура ОбработкаТаблицы(Таблица) Экспорт
Для каждого СтрокаТаблицы из Таблица Цикл
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
ПРАВИЛЬНО:
// Параметры:
// Таблица - ТаблицаЗначений - произвольная таблица с товаром:
// * Номенклатура - СправочникСсылка.Номенклатура
Процедура ОбработкаТаблицы(Таблица) Экспорт
Для каждого СтрокаТаблицы из Таблица Цикл
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
- Исключением являются методы обрабатывающие таблицу значений с неопределенным набором колонок, например "копирование таблицы в структуру", при этом код не обращается к колонкам напрямую
СтрокаТЗ.Номенклатура = ...
а использует для обращения список колонок, сформированный в рантаймеСтрокаТЗ[ИмяКолонки] = ...
.
Описание строки таблицы или дерева значений
Следует использовать один из двух вариантов:
- прямое указание колонок, на которые рассчитывает код в данной процедуре
НЕПРАВИЛЬНО:
// Параметры:
// СтрокаТаблицы - СтрокаТаблицыЗначений
Процедура ОбработкаТаблицы(СтрокаТаблицы)
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
ПРАВИЛЬНО:
// Параметры:
// СтрокаТаблицы - СтрокаТаблицыЗначений:
// * Номенклатура - СправочникСсылка.Номенклатура
Процедура ОбработкаТаблицы(СтрокаТаблицы)
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
- ссылка на строку ТЗ из функции-конструктора таблицы значений:
ПРАВИЛЬНО:
// Параметры:
// СтрокаТаблицы - СтрокаТаблицыЗначений: См. НоваяТаблицаСНоменклатурой
Процедура ОбработкаТаблицы(СтрокаТаблицы)
СтрокаТаблицы.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
....
// Возвращаемое значение:
// ТаблицаЗначений - с колонками:
// * Номенклатура - СправочникСсылка.Номенклатура -
Функция НоваяТаблицаСНоменклатурой()
....
Описание соответствия
- Соответствие - это фактически сложный тип с фиксированным набором колонок (ключ и значение) или аналогичен массиву из структур с 2 колонками. При этом описание типов у ключа и значения есть по умолчанию -
Произвольный
, и необходимо создавать функцию конструктор которая инициализирует соответствие и описывает типы ключа и значения. - Следует избегать комбинации различных типов в ключе и в значении соответствия.
ПРАВИЛЬНО:
// Возвращаемое значение:
// Соответствие из КлючИЗначение:
// * Ключ - Строка
// * Значение - Массив из ДокументОбъект
Функция НовоеСоответствие()
...
Ссылка на Табличную часть объекта
ПРАВИЛЬНО:
// Параметры:
// Объект - См. Справочник.Товары.ЕдиницыИзмерения
Процедура ОбработкаОбъекта(Объект)
Ссылка на Строку Табличной части объекта
Такая ссылка еще не поддерживается в 1C:EDT!
ПРАВИЛЬНО:
// Параметры:
// Объект - СтрокаТабличнойЧасти: См. Справочник.Товары.ЕдиницыИзмерения
Процедура ОбработкаОбъекта(Объект)
Ссылка на тип реквизита объекта или реквизита Табличной части объекта
ПРАВИЛЬНО:
// Параметры:
// Реквизит1 - См. Справочник.Товары.Артикул
Процедура ОбработкаОбъекта(Реквизит1)
ПРАВИЛЬНО:
// Параметры:
// РеквизитТЧ - См. Справочник.Товары.ЕдиницыИзмерения.Единица
Процедура ОбработкаОбъекта(РеквизитТЧ)
Ссылка на Форму
Ссылка на форму указывается по полному имени формы
ПРАВИЛЬНО:
// Параметры:
// Форма - См. Справочник.Товары.Форма.ФормаЭлемента
Процедура ОбработкаОбъекта(Форма)
Ссылка на Элемент Формы, Реквизит формы
Ссылка тип реквизита формы
ПРАВИЛЬНО:
// Параметры:
// Объект - См. Справочник.Товары.Форма.ФормаЭлемента.Объект
Процедура ОбработкаОбъекта(Объект)
Ссылка тип элемента формы
ПРАВИЛЬНО:
// Параметры:
// Список - См. Справочник.Товары.Форма.ФормаСписка.Элементы.Список
Процедура ОбработкаОбъекта(Список)
Ссылка на Текущие данные динамического списка формы
Использование ссылки на тип текущих данных динамического списка на форме не поддерживается в документирующих комментариях.
В место этого следует передавать сам элемент формы и далее обрабатывать ТекущиеДанные
ПРАВИЛЬНО:
// Параметры:
// Список - См. Справочник.Товары.Форма.ФормаСписка.Элементы.Список
Процедура ОбработкаОбъекта(Список)
ЗначениеАртикула = Список.ТекущиеДанные.Артикул;
...
Ссылка на Строку таблицы значений формы
Такая ссылка еще не поддерживается в 1C:EDT!
ПРАВИЛЬНО:
// Параметры:
// СтрокаТЗ - ДанныеФормыЭлементКоллекции: См. Справочник.Товары.Форма.ФормаСписка.Элементы.ТЗ
Процедура ОбработкаОбъекта(СтрокаТЗ)
Ссылка на тип параметра метода в модели менджера, объекта, общем модуле
Ссылка на тип параметра экспортного метода из модуля менеджера
ПРАВИЛЬНО:
// Параметры:
// Список - См. Справочники.Товары.МетодМодуляМенеджера.Параметр1
Процедура ОбработкаОбъекта(Список)
Ссылка на тип параметра экспортного метода из модуля объекта
ПРАВИЛЬНО:
// Параметры:
// Список - См. СправочникОбъект.Товары.МетодМодуляОбъекта.Параметр2
Процедура ОбработкаОбъекта(Список)
Наследование типов параметров по сигнатуре метода
При размещении процедур в общих модулях (обычно "Переопределяемый") из общих процедуры могут быть вызваны наследники какого-либо механизма библиотеки.
ПРАВИЛЬНО:
// Параметры:
// Список - См. СправочникОбъект.Товары.МетодМодуляОбъекта.Параметр2
// Реквизит - См. Справочник.Товары.ЕдиницыИзмерения.Единица
// Объект - СправочникОбъект.Товары
Процедура ОбработкаОбъекта(Список, Реквизит, Объект) Экспорт
Документы.РеализацияТоваров.ОбработкаОбъекта(Список, Реквизит, Объект);
Документы.ПоступлениеТоваров.ОбработкаОбъекта(Список, Реквизит, Объект);
...
В модуле менеджера соответствующих объектов можно сослаться на сигнатуру параметров метода, чтобы не описывать полностью все типы. Необходимо указать только ссылку на метод, других комментариев или элементов документирующих комментариев быть не должно.
ПРАВИЛЬНО:
// См. ОбщийМодульПодсистема1Переопределяемый.ОбработкаОбъекта
Процедура ОбработкаОбъекта(Список, Реквизит, Объект)
В этом случае будет выполнено сопоставление имен параметров текущего метода с именами параметров метода "интерфейса".
Функции-конструкторы сложных объектов данных
Для сложных типов, создаваемых на основе абстрактных платформенных типов (Структура
, Соответствие
, ТаблицаЗначений
, ДеревоЗначений
и др.), следует использовать функцию-конструктор данных.
Наименование функции следует выбирать как Новый
/Новая
/Новое
(New
) и наименование объекта данных.
ПРАВИЛЬНО:
// Возвращаемое значение:
// ТаблицаЗначений:
// * Номенклатура - СправочникСсылка.Номенклатура
Функция НоваяТаблицаОтобраннойНоменклатуры()
....
КонецФункции
- Конструктор данных может так же выполнять функцию заполнения данных т.е. предоставлять новый объект с заполненными данными. Например, функция выполняет запрос к БД и возвращает таблицу значений с колонками и данными.
Указание ссылки на функцию-конструктор данных
При описании параметра метода следует указывать ссылку на функцию-конструктор данных без указания исходного базового типа данных (структура, таблица значений и т.д.).
НЕПРАВИЛЬНО 1:
// Заполняет список команд печати.
//
// Параметры:
// КомандыПечати - ТаблицаЗначений - состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати.
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
НЕПРАВИЛЬНО 2:
// Заполняет список команд печати.
//
// состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати.
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
НЕПРАВИЛЬНО 3:
// см. УправлениеПечатью.СоздатьКоллекциюКомандПечати.
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
НЕПРАВИЛЬНО 4:
// Заполняет список команд печати.
//
// Параметры:
// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати.
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
НЕПРАВИЛЬНО 5:
// Заполняет список команд печати.
//
// Параметры:
// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати - команды для заполнения
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
ПРАВИЛЬНО:
// Заполняет список команд печати.
//
// Параметры:
// КомандыПечати - см. УправлениеПечатью.СоздатьКоллекциюКомандПечати
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
Функции-получатели сложных объектов данных
При обработке более общих типов объектов в общих модулях может потребоваться получить заранее известный комплексный тип (Структура
, ТаблицаЗначений
, ДеревоЗначений
), при этом, функции-конструктора таких данных может не существовать, например когда данные создаются объектом метаданных, формой, СКД и так далее. Для таких случаев следует использовать функцию-получатель, описывающую тип возвращаемого значения со всеми необходимыми свойствами/полями объекта.
Для клиентских процедур не рекомендуется использовать конструкторы из модулей с контекстом 'Сервер', но без контекста 'Вызов сервера'.
НЕПРАВИЛЬНО:
// Параметры:
// Форма - ФормаКлиентскогоПриложения - форма, для элементов которых производится заполнение
// Параметры - Структура - дополнительные параметры дерева.
//
Процедура ОбновитьДеревоНовыхЭлементов(Форма, Параметры) Экспорт
ИмяЭлементаДерева = Параметры.ИмяЭлементаДерева;
...
ДеревоЭлементов = Форма.РеквизитФормыВЗначение(ИмяЭлементаДерева);
ДеревоЭлементов.Строки.Очистить();
...
СтрокаЭлемент = ДеревоЭлементов.Строки.Добавить();
СтрокаЭлемент.Наименование = ...;
ПРАВИЛЬНО:
// Параметры:
// Форма - ФормаКлиентскогоПриложения - форма, для элементов которых производится заполнение
// Параметры - Структура - дополнительные параметры дерева.
//
Процедура ОбновитьДеревоНовыхЭлементов(Форма, Параметры) Экспорт
ИмяЭлементаДерева = Параметры.ИмяЭлементаДерева;
...
ДеревоЭлементов = ДеревоНовыхЭлементов(Форма, ИмяЭлементаДерева);
ДеревоЭлементов.Строки.Очистить();
...
СтрокаЭлемент = ДеревоЭлементов.Строки.Добавить();
СтрокаЭлемент.Наименование = ...;
КонецПроцедуры
// Параметры:
// ...
//
// Возвращаемое значение:
// ДеревоЗначений:
// * Наименование - Строка
// * ВидЭлемента - ...
// * ...
Функция ДеревоНовыхЭлементов(Форма, ИмяЭлементаДерева)
Возврат Форма.РеквизитФормыВЗначение(ИмяЭлементаДерева);
КонецФункции
Ограничение на использование в документирующих описаниях Массивов, ТЗ, Структур
Для параметров экспортных методов рекомендуется указывать ссылку на функцию-конструктор таких объектов.
Не рекомендуется указывать в качестве параметров не экспортных методов сложные типы на основе Массивов
, Таблиц Значений
, Структур
и т.д., если объект является цельным, созданным в текущем модуле и контекст передачи объекта ограничен текущим модулем. В этом случае 1C:EDT не выполняет динамическую типизацию таких параметров, а использует указанные статические типы в описании метода.
Допускается описание таких типов во входящих параметрах, если описывается часть объекта или программный интерфейс объекта, например несколько колонок ТЧ.Товары
для которых выполняется расчет, при этом не существует одной единой функции-конструктора на которую можно сослаться в описании.
Описание программных интерфейсов для реализации в прикладных объектах
Существует понятие "описания программного интерфейса объекта" и его реализация в прикладных объектах конфигурации. С применением библиотечного подхода к разработке конфигураций, библиотека может описывать некий программный интерфейс который должен быть реализован в прикладном объекте, для того чтобы библиотечный механизм имел доступ к объекту.
Например, механизм "Свойства" из библиотеки БСП описывает интерфейс объекта: Наличие табличной части ДополнительныеРеквизиты
, содержит колонки Свойство
, Значение
, ТекстоваяСтрока
с определенными типами. Далее общий механизм библиотеки может обращаться к объекту например, при записи УправлениеСвойствами.ПередЗаписьюНаСервере(ЭтотОбъект, ТекущийОбъект);
объекта из формы.
Следует описывать интерфейс входящего объекта для той части объекта, которая требуется для работы механизма. При этом, следует учитывать, что механизм расширений свойств в документирующих коментариях поддерживается для тех типов, у которых возможны пользовательские совойства. Например: элементы коллекции вместо самих коллекций - тип ТабличнаяЧасть
не имеет пользовательских свойств, а тип СтрокаТабличнойЧасти
может иметь; тип ДанныеФормыКоллекция
не имеет пользовательских свойств, а тип ДанныеФормыЭлементКоллекции
может иметь.
НЕПРАВИЛЬНО:
// Параметры:
// Форма - ФормаКлиентскогоПриложения - уже настроена в процедуре ПриСозданииНаСервере:
// * Элементы - ВсеЭлементыФормы:
// ** ДополнительныеРеквизиты - ГруппаФормы
// * Свойства_ИспользоватьСвойства - Булево
// * Свойства_ИспользоватьДопРеквизиты - Булево
// ....
//
// Объект - Неопределено - взять объект из реквизита формы "Объект".
// - СправочникОбъект, ДокументОбъект, ДанныеФормыСтруктура - (по типу объекта) где:
// * ДополнительныеРеквизиты - ТабличнаяЧасть, ДанныеФормыКоллекция:
// ** Свойство - ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения -
// ** Значение - Характеристика.ДополнительныеРеквизитыИСведения -
// ** ТекстоваяСтрока - Строка -
Процедура ПеренестиЗначенияИзРеквизитовФормыВОбъект(Форма, Объект = Неопределено, ПередЗаписью = Ложь) Экспорт
Если НЕ Форма.Свойства_ИспользоватьСвойства
ИЛИ НЕ Форма.Свойства_ИспользоватьДопРеквизиты Тогда
...
СтарыеЗначения = Объект.ДополнительныеРеквизиты.Выгрузить();
Объект.ДополнительныеРеквизиты.Очистить();
...
ПРАВИЛЬНО:
// Параметры:
// Форма - ФормаКлиентскогоПриложения - уже настроена в процедуре ПриСозданииНаСервере:
// * Элементы - ВсеЭлементыФормы:
// ** ДополнительныеРеквизиты - ГруппаФормы
// * Свойства_ИспользоватьСвойства - Булево
// * Свойства_ИспользоватьДопРеквизиты - Булево
// ....
//
// Объект - Неопределено - взять объект из реквизита формы "Объект".
// - СправочникОбъект, ДокументОбъект, ДанныеФормыСтруктура - (по типу объекта) где:
// * ДополнительныеРеквизиты - ТабличнаяЧасть Из СтрокаТабличнойЧасти:
// ** Свойство - ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения -
// ** Значение - Характеристика.ДополнительныеРеквизитыИСведения -
// ** ТекстоваяСтрока - Строка -
Процедура ПеренестиЗначенияИзРеквизитовФормыВОбъект(Форма, Объект = Неопределено, ПередЗаписью = Ложь) Экспорт
Если НЕ Форма.Свойства_ИспользоватьСвойства
ИЛИ НЕ Форма.Свойства_ИспользоватьДопРеквизиты Тогда
...
СтарыеЗначения = Объект.ДополнительныеРеквизиты.Выгрузить();
Объект.ДополнительныеРеквизиты.Очистить();
...
Описание используемых реквизитов и элементов форм для общего кода по работе с формами
- Допускается описание части формы, общие для нескольких форм или реализация некого интерфейса для общего механизма конфигурации. Например, части одного общего механизма подключенные к множеству объектов конфигурации (дополнительные реквизиты, контактная информация и т.д.).
ПРАВИЛЬНО:
// Параметры:
// Форма - ФормаКлиентскогоПриложения:
// * Объект - ДанныеФормыСтруктура, СправочникОбъект, ДокументОбъект - основной реквизит формы
// * Элементы - ВсеЭлементыФормы:
// ** Товары - ТаблицаФормы - элемент таблицы товаров
Процедура ПриСозданииНаСервере(Форма)
Ссылка = Форма.Объект.Ссылка;
ТекущиеДанные = Форма.Элементы.Товары.ТекущиеДанные;
....
- Если используется конкретная форма, следует в документирующих комментариях модулей (общих, менеджеров) указывать полную ссылку на форму.
// Параметры:
// Форма - см. Справочник.Номенклатура.Форма.ФормаЭлемента
Процедура ПриСозданииНаСервере(Форма)
Ссылка = Форма.Объект.Ссылка;
Форма.Элементы.Артикул.Видимость = Истина;
....
- Для функций общих модулей, рассчитывающих на дополнительные свойства и методы расширения типа
ФормаКлиентскогоПриложения
следует использовать соответствующие типы расширений управляемой формы:РасширениеУправляемойФормыДляОбъектов
,РасширениеУправляемойФормыДляДокумента
,РасширениеУправляемойФормыДляДинамическогоСписка
и так далее.
// Параметры:
// Форма - РасширениеУправляемойФормыДляОбъектов -
Процедура ОбработкаЗакрытия(Форма) Экспорт
....
Форма.Записать();
....
Получение и открытие формы
- При использовании методов
ПолучитьФорму()
иОткрытьФорму()
с присвоением возвращаемого значения формы после открытия - следует указывать строковый литерал с полным именем формы первым параметром. - Не следует выносить строковый литерал в отдельную переменную т.к. в этом случае переменная форма будет содержать общий тип
ФормаКлиентскогоПриложения
а не конкретный тип формы справочника номенклатуры, и весь контекст формы не будет доступен. В 1C:EDT на текущий момент поддерживается полная типизация только для полных имен форм, ссылки на основные формы для объекта метаданногоФормаОбъекта
,ФормаСписка
и т.д. возвращают общий типФормаКлиентскогоПриложения
, полная типизация может быть поддержана в будущих версиях.
НЕПРАВИЛЬНО:
ИмяФормы = "Справочник.Номенклатура.Форма.ФормаЭлемента";
Форма = ПолучитьФорму(ИмяФормы);
ПРАВИЛЬНО:
Форма = ПолучитьФорму("Справочник.Номенклатура.Форма.ФормаЭлемента");
// или
Форма = Справочники.Номенклатура.ПолучитьФорму("ФормаЭлемента");
Экспортные процедуры и функции
- Все параметры методов и возвращаемые значения функций должны содержать описания типов, при этом пояснения к параметрам следует описывать в соответствии со стандартом [?]
- Локальные процедуры и функции не обязаны содержать описаний типов в документирующих комментариях и могут быть полностью расчетными, если код написан прозрачно для статического анализатора.
Разрыв прямого контекста выполнения кода
- В случае разрыва прямого вызова методов в рамках одного модуля (когда из одного метода напрямую вызывается другой метод) - следует описывать типы входящих параметров для не экспортных методов.
Использование временного хранилища и других контейнеров
- Поместить во временное хранилище и после получить из него - можно все что угодно, поэтому необходимо выделять функцию возвращающую тип помещенный во временное хранилище использовать на нее ссылку при получении:
Данные = ПолучитьИзВременногоХранилища(Адрес); // см. НовыйОбъектДанных
- Аналогичный подход следует использовать при помещении пользовательского объекта внутрь другого объекта-контейнера, например
ДополнительныеПараметры
у объектов, илиПараметры
формы, пользовательские параметры элементовСКД
,ДополнительныеПараметры
у обработчиков оповещения и так далее.
Использование строковых литералов в качестве имен
Не следует обращаться к элементу именованной коллекции, например ВсеЭлементыФормы
и другие, через строковый индекс с именем элемента. Вместо этого следует обращаться напрямую к элементу так как в этом случае статический анализатор может контролировать наличие элемента, его тип.
НЕПРАВИЛЬНО:
Элементы["Наименование"].Видимость = Ложь;
ПРАВИЛЬНО:
Элементы.Наименование.Видимость = Ложь;
Исключением может быть:
- обращение к элементам и свойствам создаваемым программно и в момент статического анализа их еще не существует. При этом следует указывать тип локальной переменной полученного значения и уже потом обращаться к ее свойства:
НЕПРАВИЛЬНО:
Элементы["Наименование"].Видимость = Ложь;
Элементы["Наименование"].Доступность = Истина;
ПРАВИЛЬНО:
Элемент = Элементы["Наименование"]; // ПолеФормы -
Элемент.Видимость = Ложь;
Элемент.Доступность = Истина;
- наличие таких элементов в коллекции может быть опциональным, при этом выполняется проверка наличия элемента. Например: исходный объект с неопределенными свойствами создается где-то вне и передается в текущую функцию входящим параметром.
ПРАВИЛЬНО:
Элемент = Элементы.Найти("Наименование");
Если Элемент <> Неопределено Тогда
Элемент.Видимость = Ложь;
...
ПРАВИЛЬНО:
Если Параметры.Свойство("Ссылка") Тогда
Ссылка = Параметры["Ссылка"]; // СправочникСсылка -
...
Ограничение на использование реквизитов формы с типом "Произвольный"
Реквизит формы с типом Произвольный
в коде следует рассматривать как "черный ящик". Статический анализатор не отслеживает изменение типа реквизита формы, поэтому весь обслуживающий этот реквизит код должен использовать проверку типа значения.
В общем случае не следует использовать реквизит с типом Произвольный
на форме, т.к. при этом разработчик самостоятельно несет ответственность за инициализацию значения в этом реквизите, за то, что все типы данных хранимые в реквизите могут быть сериализованы/десериализованы при передаче с клиента на сервер и обратно. Так же значение реквизита будет передаваться между клиентом и сервером всегда т.к. Платформа не контролирует модификацию значений, например внутри структуры, помещенной в произвольный реквизит.
Реквизит с типом Произвольный
следует заменять на честные реквизиты формы с определенными типами. Исключением может быть хранимые данные общих механизмов библиотек, которые через программный интерфейс инициализируют и обрабатывают хранимое в этом реквизите значение.
Если заменить реквизит с типом Произвольный
нет возможности, следует использовать функцию-конструктор для инициализации значения и дальнейших ссылок на типы. При этом не следует напрямую обращаться к реквизиту - для получения значения следует использовать функцию-получатель.
ПРАВИЛЬНО:
// Возвращаемое значение:
// Структура:
// * Ссылка - СправочникСсылка.Номенклатура - ссылка на текущую номенклутру
// ...
Функция НовыйСложныйОбъектДанных()
Данные = Новый Структура;
Данные.Вставить("Ссылка", ...);
...
Возврат Данные;
КонецФункции
// Возвращаемое значение:
// см. НовыйСложныйОбъектДанных
Функция РеквизитПроизвольный()
Возврат РеквизитПроизвольный;
КонецФункции
...
// Инициализация реквизита через функцию-конструктор
РеквизитПроизвольный = НовыйСложныйОбъектДанных();
// Обращение к значению в реквизите с произвольным типом
Ссылка = РеквизитПроизвольный().Ссылка;
Использование параметров формы
- Запрещается использовать параметр с типом "Произвольный" для передачи структуры параметров для инициализации формы.
- В редакторе формы на вкладке "Параметры" следует описывать все параметры, на которые опирается форма при открытии, включая необязательные.
Выборка и выгрузка из результата запроса
При типизации выборки/выгрузки из результата запроса следует использовать возможности:
- Статическое описание всех полей выборки (полей таблицы значений выгрузки) в возвращаемом значении функции-получателе данных. Может применяться для функций, динамически формирующих текст запроса и выборку, а так же все экспортные функции, возвращающие выборку.
- Динамическая типизация полей выборки на основе текста запроса. В будущих версиях 1C:EDT может быть реализована возможность обращаться к полям запроса, при условии что текст запроса и выборка (результат запроса) находятся в одной процедуре, текст запроса не содержит ошибок (открывается "Конструктором запросов"), выборка/выгрузка из результата запроса не передается в другой модуль для обработки.
НЕПРАВИЛЬНО:
Запрос = Новый Запрос;
Запрос.Текст =
ТексЗапросаОстатков()
+ ТекстЗапросаРезервов();
РезультаЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Ссылка = Выборка.Ссылка.ПолучитьОбъект();
...
ПРАВИЛЬНО:
// Возвращаемое значение:
// ВыборкаИзРезультатаЗапроса:
// * Номенклатура - СправочникСсылка.Номенклатура
Функция ОстаткиДляОбработки()
Запрос = Новый Запрос;
Запрос.Текст =
ТексЗапросаОстатков()
+ ТекстЗапросаРезервов();
РезультаЗапроса = Запрос.Выполнить();
Возврат РезультатЗапроса.Выбрать();
КонецФункции;
...
Выборка = ОстаткиДляОбработки();
Пока Выборка.Следующий() Цикл
Ссылка = Выборка.Номенклатура.ПолучитьОбъект();
...
ПРАВИЛЬНО:
Запрос = Новый Запрос;
Запрос.Текст = "
| ВЫБРАТЬ
| Номенклатура
|ИЗ
| РегистраНакопления.Остатки.Остатки
|
| ОБЪДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Номенклатура
|ИЗ
| РегистрНакопления.Резервы.Остатки
|";
РезультаЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Ссылка = Выборка.Номенклатура.ПолучитьОбъект();
...
Передача пользовательского объекта данных в другой модуль
Объекты данных созданные пользовательским кодом и использованные вне текущего модуля - должны быть описаны функцией возвращающей значение.
Использование типа созданного в текущем модуле, но не предназначенного для создания в другом модуле - следует использовать не экспортную функцию-конструктор, при этом существует 2 варианта:
- Экспортная функция, которая возвращает это значение вместе с данными (функция-получатель) - в ней нужно в возвращаемом значении указать функцию-конструктор
- Реализация какого-либо интерфейса из переопределяемого модуля - в этом случае, можно использовать ссылку на параметр
Описание типов макетов СКД и Табличного документа
- В случае получения макета через универсальную процедуру - следует указывать в строке тип макета.
Макет = УправлениеПечатью.МакетПечатнойФормы("Документ.РеализацияТоваровУслуг.ПечатнаяФорма"); //см. Документ.РеализацияТоваровУслуг.Макет.ПечатнаяФорма
Ограничение на создание пользовательских объектов-копий (Структуры, ТЗ) из платформенных объектов
- Существуют процедуры которые создают ТЗ или структуру аналогичную какому-нибудь объект метаданных, далее выполняется копирование значений для каждого свойства
- Не рекомендуется, т.к. Необходимо будет описать весь объект самостоятельно
- В исключительных случаях (каких?) можно сделать функцию-конструктор описывающую смешанный тип
Структура + СправочникОбъект.Номенклатура
чтобы не описывать весь тип. При этом можно добавить в описание дополнительные колонки, которые вводятся для технических целей (помощь при копировании, передача дополнительной информации с объектом). - Следует учитывать ограничение на описание строк ТЧ объектов метаданных.
Ограничения составных типов
- В общем случае неправильно делать составные типы, которые мало похожи между собой. Например:
- Строка и СправочникОбъект
- Число и Массив
- Коллекции (массив, ТЗ, Соответствие, ТЧ, Структура) и простые типы (строка, число, ссылка)
- ДеревоЗначений и ТаблицаЗначений
- Наличие таких смешанных типов - однозначный повод для рефакторинга кода.