Почему при сложении всё работает, а при вычитании в 1С возникает ошибка «Преобразование значения к типу Число не может быть выполнено»?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интерактивных элементов интерфейса, таких как калькуляторы или формы быстрого расчета, начинающие разработчики часто сталкиваются с неожиданным поведением платформы 1С. Типичный сценарий: операция сложения выполняется абсолютно корректно, но при попытке вычесть, умножить или разделить числа программа аварийно завершает работу с ошибкой «Преобразование значения к типу Число не может быть выполнено».

В этой статье мы подробно разберем, почему возникает эта иллюзия «работающего сложения», проанализируем внутренние механизмы приведения типов в платформе 1С, научимся локализовать проблему с помощью отладчика и рассмотрим профессиональные способы написания надежного кода для математических вычислений.

Анализ проблемного исходного кода

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


&НаКлиенте
Процедура Сложение(Команда)
    Объект.Операция = "Сложение";
    Попытка
        Объект.Буфер = Число(Объект.Табло) + Число(Объект.ТаблоКалькулятора);
    Исключение
        Объект.Буфер = Объект.Табло + Объект.ТаблоКалькулятора;
    КонецПопытки;
    Объект.Табло = Объект.Буфер;
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = "";
КонецПроцедуры

&НаКлиенте
Процедура Вычитание(Команда)
    Объект.Операция = "Вычитание";
    Попытка
        Объект.Буфер = Число(Объект.Табло) - Число(Объект.ТаблоКалькулятора);
    Исключение
        Объект.Буфер = Объект.Табло - Объект.ТаблоКалькулятора;
    КонецПопытки;
    Объект.Табло = Объект.Буфер;
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = "";
КонецПроцедуры

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

В чем скрытая ловушка конструкции Попытка — Исключение?

Конструкция Попытка — Исключение предназначена для обработки исключительных ситуаций, которые программист не может предотвратить проверками (например, сетевые сбои, блокировки базы данных). Использовать ее для маскировки ошибок приведения типов — крайне нерекомендуемая практика (так называемый «антипаттерн»).

Выясним по шагам, что происходит во время выполнения процедуры Сложение, когда реквизит Объект.ТаблоКалькулятора содержит пустую строку "":

  1. Программа заходит в блок Попытка.
  2. Платформа пытается выполнить преобразование Число(Объект.ТаблоКалькулятора). Так как пустая строка не может быть преобразована в число, система генерирует исключение.
  3. Управление мгновенно передается в блок Исключение, минуя сложение чисел.
  4. В блоке Исключение выполняется код: Объект.Буфер = Объект.Табло + Объект.ТаблоКалькулятора;.
  5. Поскольку оба реквизита имеют строковый тип (например, "5" и ""), оператор + в языке 1С выполняет строковую конкатенацию (склеивание строк).
  6. Результатом склеивания "5" + "" становится строка "5". Операция завершается успешно, без системных ошибок. Разработчику кажется, что «калькулятор считает правильно», хотя на самом деле математического сложения не произошло — выполнилась работа со строками.

Почему ломается процедура Вычитание?

Теперь проанализируем работу процедуры Вычитание в аналогичных условиях (когда один из операндов пуст):

  1. Программа заходит в блок Попытка.
  2. Попытка преобразования пустой строки к числу завершается неудачей. Происходит переход в блок Исключение.
  3. В блоке Исключение выполняется код: Объект.Буфер = Объект.Табло - Объект.ТаблоКалькулятора;.
  4. Платформа пытается выполнить операцию вычитания над строковыми переменными (например, "10" - "").
  5. В отличие от сложения, оператор вычитания - (а также умножения и деления) не определен для строковых типов данных. Строки нельзя «вычитать» друг из друга.
  6. Платформа 1С пытается применить механизм неявного (автоматического) приведения типов. Она пробует самостоятельно преобразовать обе строки к типу Число, чтобы выполнить арифметическое действие.
  7. Неявное преобразование пустой строки "" к числу ожидаемо завершается аварийно.
  8. Поскольку эта ошибка происходит уже внутри самого блока Исключение, и она не обернута во внешнюю попытку, платформа прекращает работу процедуры и выдает пользователю системное сообщение: «Преобразование значения к типу Число не может быть выполнено».

Таким образом, иллюзия «работающего сложения» возникала исключительно из-за того, что знак плюс умеет соединять строки, а знак минус — нет.

Использование отладчика для локализации проблемы

Для того чтобы наглядно увидеть этот процесс, настроим и используем встроенный отладчик 1С. Пройдем по шагам:

  1. Откроем конфигуратор и найдем модуль управляемой формы калькулятора.
  2. Установим точку останова на первой строке процедуры Вычитание (клавиша F9).
  3. Запустим сеанс отладки (клавиша F5).
  4. В открывшемся пользовательском интерфейсе введем числа и нажмем кнопку вычитания.
  5. После того как управление передастся в конфигуратор, будем пошагово выполнять код с помощью клавиши F8 (Шагнуть обходом).
  6. Обратим внимание на то, как указатель текущей строки перепрыгнет со строки с функциями Число() сразу на строку внутри блока Исключение.
  7. Выделим выражение Объект.ТаблоКалькулятора, нажмем Shift + F9 (Быстрое вычисление выражения) и убедимся, что тип значения — Строка, а само значение пустое.

Этот простой эксперимент наглядно демонстрирует, что логика приложения пошла по незапланированному сценарию.

Как правильно исправить код калькулятора?

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

Решение 1. Строгая типизация реквизитов (Рекомендуемый способ)

Самый простой и надежный способ избежать подобных ошибок — хранить данные в правильном типе с самого начала. Если реквизиты Табло, ТаблоКалькулятора и Буфер предназначены исключительно для математических расчетов, они должны иметь тип Число.

Выполним настройку реквизитов:

  1. В дереве метаданных конфигурации или в списке реквизитов формы выберем реквизиты Табло, ТаблоКалькулятора и Буфер.
  2. В палитре свойств установим для них тип Число (зададим необходимую длину, например, 15, и точность 2 или 3 знака после запятой).

При таком подходе платформа 1С автоматически инициализирует эти поля нулями вместо пустых строк, а также заблокирует ввод любых недопустимых символов в текстовые поля на форме. Код процедур упростится до предела и избавится от громоздких «Попыток»:


&НаКлиенте
Процедура Сложение(Команда)
    Объект.Операция = "Сложение";
    Объект.Буфер = Объект.Табло + Объект.ТаблоКалькулятора;
    Объект.Табло = Объект.Буфер;
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = 0; // Теперь здесь хранится число
КонецПроцедуры

&НаКлиенте
Процедура Вычитание(Команда)
    Объект.Операция = "Вычитание";
    Объект.Буфер = Объект.Табло - Объект.ТаблоКалькулятора;
    Объект.Табло = Объект.Буфер;
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = 0;
КонецПроцедуры

Решение 2. Программная валидация и безопасное приведение типов

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

Создадим вспомогательную функцию, которая будет безопасно извлекать числовое значение из строки, обрабатывая любые пустые или некорректно заполненные поля:


&НаКлиенте
Функция ПолучитьБезопасноеЧисло(ЗначениеСтрокой)
    
    // Удаляем лишние пробелы и неразрывные пробелы, которые часто копируются из таблиц
    ЧистаяСтрока = СокрЛП(ЗначениеСтрокой);
    ЧистаяСтрока = СтрЗаменить(ЧистаяСтрока, Символы.НПП, "");
    
    // Если строка пустая, возвращаем ноль
    Если ПустаяСтрока(ЧистаяСтрока) Тогда
        Возврат 0;
    КонецЕсли;
    
    // Проверяем возможность преобразования
    Попытка
        Возврат Число(ЧистаяСтрока);
    Исключение
        // В случае ввода некорректных символов, сообщаем пользователю и возвращаем 0
        ПоказатьПредупреждение(, "Введено некорректное числовое значение! Автоматически заменено на 0.");
        Возврат 0;
    КонецПопытки;
    
КонецФункции

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


&НаКлиенте
Процедура Сложение(Команда)
    Объект.Операция = "Сложение";
    
    Число1 = ПолучитьБезопасноеЧисло(Объект.Табло);
    Число2 = ПолучитьБезопасноеЧисло(Объект.ТаблоКалькулятора);
    
    Объект.Буфер = Число1 + Число2;
    Объект.Табло = Объект.Буфер;
    
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = "";
КонецПроцедуры

&НаКлиенте
Процедура Вычитание(Команда)
    Объект.Операция = "Вычитание";
    
    Число1 = ПолучитьБезопасноеЧисло(Объект.Табло);
    Число2 = ПолучитьБезопасноеЧисло(Объект.ТаблоКалькулятора);
    
    Объект.Буфер = Число1 - Число2;
    Объект.Табло = Объект.Буфер;
    
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = "";
КонецПроцедуры

Дополнительно: Безопасное деление

Раз уж мы разрабатываем надежный калькулятор, обязательно предусмотрим защиту от деления на ноль. Это еще одна классическая ошибка, способная уронить сеанс пользователя. Напишем процедуру деления с предварительной проверкой делителя:


&НаКлиенте
Процедура Деление(Команда)
    Объект.Операция = "Деление";
    
    Число1 = ПолучитьБезопасноеЧисло(Объект.Табло);
    Число2 = ПолучитьБезопасноеЧисло(Объект.ТаблоКалькулятора);
    
    Если Число2 = 0 Тогда
        ПоказатьПредупреждение(, "Ошибка: Деление на ноль невозможно!");
        Возврат;
    КонецЕсли;
    
    Объект.Буфер = Число1 / Число2;
    Объект.Табло = Объект.Буфер;
    
    ВычислитьОтвет();
    Объект.ТаблоКалькулятора = "";
КонецПроцедуры

Подведем итоги

Мы выяснили истинную причину избирательного поведения платформы 1С при работе со строками и математическими операторами в блоках Попытка — Исключение.

Главные правила безопасной разработки математических модулей:

← На главную