Переменные во встроенном языке 1С:Предприятие 8.3
Содержание
Переменную в 1С объявляют ключевым словом Перем или просто присваиванием значения: Ставка = 0.2;. Второй вариант создаёт переменную прямо в точке первого присваивания, и для локальных значений внутри процедуры он работает так же надёжно, как явное объявление.
Разница в деталях: Перем нужен, когда переменная должна жить на уровне всего модуля или быть видимой из других модулей через Экспорт. Ниже — синтаксис объявления, как устроена типизация во встроенном языке, чем отличаются локальные переменные от переменных модуля, как передаются параметры в процедуры и на чём чаще всего спотыкаются.
Два способа создать переменную в коде
Во встроенном языке 1С переменную объявляют двумя путями.
Явное объявление — ключевое слово Перем:
Перем Счётчик;
Перем ИмяКлиента, СуммаДокумента;
Через запятую в одной строке объявляют сразу несколько переменных. Явное объявление обязательно, если переменная должна быть переменной модуля (см. раздел про область видимости) или экспортироваться.
Неявное объявление — первое присваивание:
Количество = 10;
Сообщение = "Документ проведён";
Переменная Количество создаётся в момент, когда ей присвоили значение. Отдельная строка с Перем для локальной переменной внутри процедуры не нужна — её можно сразу инициализировать.
Для локальных переменных в теле процедуры большинство разработчиков обходятся присваиванием без Перем. Явное объявление оставляют для переменных модуля и для случаев, когда хочется собрать список всех используемых имён в начале процедуры.
Правила именования простые: имя начинается с буквы или подчёркивания _, дальше — буквы, цифры и подчёркивания. Пробелы и знаки недопустимы. Регистр не важен: Сумма и СУММА — одна и та же переменная. Нельзя использовать зарезервированные слова (Если, Цикл, Процедура и т.д.).
Встроенный язык поддерживает русский и английский синтаксис: Перем и Var работают одинаково. В типовых конфигурациях принят русский вариант и запись имён в стиле СуммаБезНДС (каждое слово с заглавной буквы).
Почему у переменной не указывают тип
Встроенный язык 1С — динамически типизированный. Это значит, что тип не привязывается к переменной при объявлении. Тип определяется тем значением, которое в переменную положили, и может меняться по ходу выполнения:
Значение = 100; // здесь это Число
Значение = "Отменено"; // теперь та же переменная хранит Строку
Значение = Истина; // а теперь Булево
Конструкции вроде Перем Счётчик Как Число в языке нет — указать тип при объявлении нельзя. Проверка типов происходит во время выполнения: если попытаться сложить число со строкой или вызвать у значения несуществующий метод, ошибка возникнет в момент исполнения строки, а не при компиляции.
Основные типы значений, которые получает переменная:
- 🔢
Число— целые и дробные; - 🔤
Строка— текст; - ✅
Булево—Истина/Ложь; - 📅
Дата— дата и время; - ⬜
Неопределено— «значение не задано»; - 🚫
Null— отсутствие данных (приходит из запросов к базе); - 📦 коллекции —
Массив,Структура,Соответствие,СписокЗначений,ТаблицаЗначений; - 🔗 ссылки на объекты базы — элементы справочников, документы и т.д.
Где типизация действительно строгая — это реквизиты объектов метаданных. У реквизита справочника или документа тип фиксируется в конфигураторе на этапе проектирования, и положить в него значение другого типа платформа не даст. Но это свойство метаданных, а не переменных в коде.
Узнать тип значения в переменной помогает функция ТипЗнч():
Если ТипЗнч(Значение) = Тип("Число") Тогда
// обрабатываем число
КонецЕсли;
Область видимости: локальные и переменные модуля
От того, где объявлена переменная, зависит, кто её «видит».
Локальные переменные живут внутри одной процедуры или функции. Создаются присваиванием или через Перем в теле процедуры, исчезают после выхода из неё. Снаружи к ним обратиться нельзя:
Процедура РассчитатьИтог()
Промежуток = 500; // локальная, видна только здесь
КонецПроцедуры
Переменные модуля объявляются через Перем в самом начале модуля — до первой процедуры или функции. Они видны во всех процедурах этого модуля и сохраняют значение между их вызовами:
Перем ОбщийСчётчик; // переменная модуля, в начале модуля
Процедура Увеличить()
ОбщийСчётчик = ОбщийСчётчик + 1;
КонецПроцедуры
Экспорт — выход за пределы модуля. Если к переменной модуля добавить Экспорт, она станет доступна снаружи — через ссылку на объект модуля:
Перем ТекущийПользователь Экспорт;
Это и есть самая широкая область видимости в языке. Отдельного ключевого слова «глобальная переменная» в 1С нет: нельзя написать строку, которая создаст имя, автоматически видимое из любого места программы.
Общие модули (Общие модули в дереве конфигурации) переменные модуля не поддерживают — в них допустимы только процедуры и функции. Поэтому хранить «глобальное» состояние через переменную общего модуля не получится.
Если нужно общее для всей сессии значение, применяют другие механизмы: параметры сеанса (ПараметрыСеанса), экспортные переменные модуля управляемого приложения (живут на клиенте в течение сеанса) или функции общего модуля, возвращающие нужные данные. Выбор зависит от того, на клиенте или на сервере должно жить значение.
Коротко три уровня видимости выглядят так:
| Область | Как объявить | Кто видит | Когда исчезает |
|---|---|---|---|
| Локальная | присваивание или Перем в теле процедуры | только эта процедура | при выходе из процедуры |
| Модуля | Перем в начале модуля | все процедуры модуля | при завершении работы с модулем |
| Экспорт | Перем … Экспорт в начале модуля | внешний код через ссылку на объект | вместе с объектом модуля |
Правило выбора простое: пока значение нужно в пределах одного расчёта — держите его локальным. Поднимать переменную на уровень модуля стоит только тогда, когда её действительно читают несколько процедур; лишние переменные модуля усложняют отладку, потому что их может изменить любая процедура.
Инициализация и присваивание значений
Объявление и инициализация — разные вещи. Перем Итог; создаёт переменную, но значение ей не задаёт. До первого присваивания объявленная переменная содержит Неопределено:
Перем Итог;
Сообщить(Итог); // выведет пустую строку — значение Неопределено
Итог = 0; // вот теперь инициализация
Итог = Итог + 100;
Оператор присваивания — знак =. Он же используется в условиях для сравнения, в 1С это один и тот же символ (отдельного == нет).
Важный момент с коллекциями: переменную-коллекцию нужно сначала создать объектом, прежде чем ею пользоваться:
Товары = Новый Массив; // создали пустой массив
Товары.Добавить("Карандаш"); // теперь можно добавлять
Если пропустить Новый Массив и сразу вызвать .Добавить, платформа сообщит об ошибке: у Неопределено нет метода Добавить.
Привычка инициализировать числовые накопители нулём (Сумма = 0;), а строковые — пустой строкой (Текст = "";) в начале блока избавляет от неожиданного Неопределено при первом сложении или конкатенации.
Параметры процедур и функций — те же переменные
Параметры, которые процедура или функция принимает в скобках, внутри неё ведут себя как локальные переменные. Отдельно объявлять их через Перем не нужно:
Функция СтоимостьСНаценкой(Цена, Наценка)
Возврат Цена + Цена * Наценка;
КонецФункции
Здесь Цена и Наценка — локальные переменные, доступные только в теле функции.
Передача по ссылке и по значению. По умолчанию параметры передаются по ссылке: если изменить параметр внутри процедуры, изменение отразится на переменной, которую передали при вызове. Чтобы передать копию и защитить исходное значение, перед параметром ставят ключевое слово Знач:
Процедура Обработать(Знач Исходные, Результат)
// Исходные менять снаружи бессмысленно — это копия
Результат = Исходные * 2; // а Результат вернётся наружу
КонецПроцедуры
Значения по умолчанию. Параметру можно задать значение на случай, если при вызове его не передали. Значением по умолчанию может быть только литерал (число, строка, Истина, Ложь, Неопределено):
Функция Округлить(Число, Разрядность = 2)
Возврат Окр(Число, Разрядность);
КонецФункции
// вызов без второго параметра — Разрядность станет 2
Итог = Округлить(125.6789);
Необязательные параметры (со значением по умолчанию) идут в списке после обязательных — нельзя поставить обязательный параметр за необязательным.
Отдельно стоит помнить про коллекции: массив, структуру или таблицу значений всегда передают по ссылке, даже без указания — сама ссылка копируется, а содержимое остаётся общим. Если внутри процедуры добавить элемент в переданный массив, он появится и в исходной переменной. Знач для коллекции копирует только ссылку, но не её элементы, поэтому полностью «заморозить» переданную таблицу одним ключевым словом не выйдет — при необходимости делают явную копию через Скопировать().
Директивы компиляции и переменные в формах
В модулях управляемых форм к объявлению переменной добавляют директиву компиляции — она говорит, где переменная существует: на клиенте или на сервере.
&НаКлиенте
Перем ВыбранныеСтроки;
&НаСервере
Перем КэшНастроек;
Основные директивы:
&НаКлиенте— переменная и код живут на стороне клиента (тонкого/веб-клиента);&НаСервере— исполняются на сервере, с доступом к данным формы;&НаСервереБезКонтекста— на сервере, но без доступа к данным формы (быстрее, подходит для вспомогательных расчётов).
Клиентская и серверная части формы не видят переменные друг друга напрямую — данные между ними передаются через реквизиты формы и параметры вызываемых процедур. Это следствие клиент-серверной архитектуры: клиент и сервер работают в разных контекстах, и переменная, объявленная &НаКлиенте, на сервере просто не существует.
Для группировки объявлений и кода внутри модуля удобны области #Область ОписаниеПеременных … #КонецОбласти — на выполнение они не влияют, но делают структуру модуля читаемой.
Типичные ошибки при работе с переменными
Несколько ситуаций, на которых спотыкаются чаще всего.
Перем после кода. Объявление переменной модуля стоит после процедуры или после исполняемых строк. Платформа требует, чтобы все Перем модуля шли в самом начале — иначе ошибка компиляции. Проверьте, что объявления собраны до первой процедуры.
Обращение к необъявленной переменной. Если имя нигде не присваивалось и не объявлялось, но используется, компилятор выдаст Переменная не определена. Частая причина — опечатка в имени: СуммаДок объявили, а читаете СумаДок.
Расчёт на «глобальность» локальной переменной. Значение, созданное присваиванием внутри процедуры, не доживёт до следующего вызова и не будет видно в другой процедуре. Если значение нужно переиспользовать — это переменная модуля.
Конкатенация числа без приведения. Склеивание числа со строкой напрямую приводит к ошибке типов. Число переводят в строку явно — например, через Формат(Сумма, "ЧГ=0") или СтрШаблон("Итог: %1", Сумма).
Изменение параметра, переданного по ссылке. Если внутри процедуры переприсвоить параметр, который получили по ссылке, снаружи переменная тоже изменится — иногда это неожиданно. Когда менять исходное значение не нужно, ставьте Знач.
Коллекция без Новый. Переменную под массив или структуру забыли создать объектом и сразу вызывают метод. Сначала Новый Массив (или Новый Структура), потом работа с элементами.
Частые вопросы
Чем `Перем` отличается от простого присваивания?
Перем только объявляет имя, не задавая значение (до инициализации там Неопределено). Присваивание Х = 5 и объявляет, и сразу инициализирует. Для локальных переменных хватает присваивания; Перем нужен для переменных модуля и экспорта.Можно ли задать переменной тип при объявлении?
Как сделать переменную глобальной, видимой во всех модулях?
Экспорт, доступная через ссылку на объект модуля. Для значений уровня сеанса используют параметры сеанса или экспортные переменной модуля приложения.Почему переменная «теряет» значение между вызовами процедуры?
Перем в начале модуля).В чём смысл ключевого слова `Знач` перед параметром?
Знач передаёт параметр по значению — внутрь процедуры попадает копия. Изменения такого параметра наружу не выходят. Без Знач параметр передаётся по ссылке, и его изменение отражается на переданной переменной.