«Вы преднамеренно строите свой код так, что ваша чувствительность к нечистоплотности делает большей вероятность того, что ваш код будет работать правильно.»
«Способ написать действительно надежный код состоит в том, чтобы пытаться использовать простые инструменты, которые учитывают типичную человеческую ненадежность, а не сложные инструменты со скрытыми побочными эффектами и дырявыми абстракциями, которые требуют безошибочности программиста.»
Джоэл Спольски ( http://www.joelonsoftware.com/articles/Wrong.html )
Не строгий синтаксис в сочетании с динамической типизацией встроенного языка 1С 7.7 на самом деле означает лишь, что контроль за синтаксисом (в той мере, в которой он смягчен) и типами переменных ложится на плечи программиста, а не интерпретатора. И программисту не стоит пренебрегать этими вещами, ведь это может привести иногда к довольно интересным эффектам работы программы и долгому поиску ошибок, причем хорошо, когда эти ошибки были выявлены еще до внедрения…
И вот, чтобы начинающие осваивать 1С, прочувствовали на примерах, к чему может привести небрежное отношение к этим вещам, приведем несколько простых правил и способов этого избежать и также рассмотрим несколько упрощенных ситуаций, в которых это может произойти, если этим правилам не следовать.
Стоит использовать в именах переменных префиксы, указывающие на признак «глобальности» переменной и тип данных, который будет использоваться для хранения в этой переменной. На подобии венгерской аннотации.
Например:
// Локальная строковая переменная процедуры или функции стрМояПеременная = "Моя строка"; // Локальная переменная процедуры или функции для хранения дат датЕщеПеременная = '08.08.08'; // Локальная переменная процедуры или функции для чисел чслПростоПеременная = 5; // Глобальная переменная глобального модуля для справочника сотрудников гл_спрСотрудники = СоздатьОбъект("Справочник.Сотрудники"); // Переменная не глобального модуля для справочника сотрудников г_спрСотрудники = СоздатьОбъект("Справочник.Сотрудники");
Тут стоит обратить внимание на то, что, если имена глобальных переменных в глобальном и не глобальном модуле будут совпадать, то в не глобальном модуле будет использоваться именно не глобальная переменная, тоже про имена функций и процедур. А вот с локальными переменными функций и процедур несколько наоборот, если имя локальной переменной будет совпадать с глобальной, то локальная переменная будет использоваться только в том случае, если она явно объявлена оператором Перем, в остальных случаях будет использоваться глобальная переменная. Но привычка явно объявлять все локальные переменные процедуры или функции, например:
Перем МояПеременная;
На самом деле не так хороша, как кажется на самом деле. Так как она несет в себе некоторые минусы в применении к 1С.
Например, если Вы явно объявите переменные и забудете их определить присвоением начальных значений, задав тем самым их тип, и начнете использовать их в выражениях или передаче параметров в функции и процедуры, то интерпретатор не сообщит Вам об этой ошибке и результат выполнения такого кода будет не совсем ожидаем Вами:
Процедура Выполнить() Перем чслПер, чслРез; чслРез = чслПер + 3; Сообщить("" + чслРез); КонецПроцедуры
Это относится и к передаче переменных в параметры функций:
Функция ПриемПараметров1(чслПар1, чслПар2, чслПар3) Возврат чслПар1 + чслПар2 + чслПар3; КонецФункции Процедура Выполнить() Перем чслПер1; // // Текст процедуры // Сообщить(""+ПриемПараметров1(чслПер1, 2, 3)); КонецПроцедуры
А вот если не иметь привычки явно объявлять, а только определять присвоением, то в таком случае, при интерпретации Вам интерпретатор выдаст ошибку: «Переменная не определена (ИмяПеременной)», что позволит отловить такие вещи еще на этапе компиляции. Например:
Процедура Выполнить() чслРез=0; чслПер1=0; чслРез = чслПер1 + чслПер2 + 3; Сообщить("" + чслРез); КонецПроцедуры
Или:
Функция ПриемПараметров1(чслПар1, чслПар2, чслПар3) Возврат чслПар1 + чслПар2 + чслПар3; КонецФункции Процедура Выполнить() чслПер1=0; // // Текст процедуры // Сообщить(""+ПриемПараметров1(чслПер1, чслПер2, 3)); КонецПроцедуры
Поэтому, я бы рекомендовал явно объявлять только глобальные переменные модулей, а локальные переменные процедур и функций, определять не явно в начале процедуры или функции начальными значениями:
Процедура Выполнить() // Объявления локальных переменных процедуры или функции стрМояПеременная = ""; // Строковая переменная датЕщеПеременная = '00.00.00'; // Переменная для хранения дат чслПростоПеременная = 0; // Переменная для чисел // Переменная для справочника сотрудников спрСотрудники = СоздатьОбъект("Справочник.Сотрудники"); // // Операторы процедуры или функции // КонецПроцедуры
Единственно, когда в таком случае, интерпретатор не выдаст Вам ошибку, если Вы без определения переменной присвоением начального значения запишете:
чслПер= чслПер+1; // Переменная не увеличится на 1, она останется пустой
Но, если в начале функции Вы будете иметь привычку присваивать начальные значения переменным, то Вы напишете:
чслПер=0; // // Операторы процедуры или функции // чслПер= чслПер+1;
И получите ожидаемый результат, не тратя усилия на воспоминания о том, имеет эта переменная числовой тип и присваивали ли Вы ей начальное значение.
И как бы нудным Вам не казалась необходимость определять в начале функции все локальные переменные, которые могут Вам понадобиться в ней с самого начала, это дисциплинирует мышление, т.к. если Вы в состоянии учесть все переменные, которые Вам понадобятся для написания процедуры или функции, значит, велика вероятность того, что Вы успешно ее напишете, т.к. это один из признаков того, что Вы четко и ясно представляете задачу и алгоритм ее решения. А в крайнем случае, если понадобилась еще одна, то не сложно подняться в начало функции и дописать строчку с присвоением ей начального значения.
Но, тут есть одно соображение, если вы явно объявляете локальные переменные, то в случае, если имя глобальной переменной совпадает с локальной, то использоваться будет именно локальная, но это не такой существенный нюанс, если Вы придерживаетесь системы в именовании переменных, функций и процедур, при которой глобальные переменные глобального модуля будут начинаться, например, с префикса «гл», а переменные модуля с префикса «г», а локальные переменные без префикса.
Так как при вызове процедур, по мимо динамической типизации переменных, еще и позволительно опускать не только параметры, которым заданы значения по умолчанию при определении функции, но и параметры без значений по умолчанию. Например, Вы вполне можете вызвать функцию таким образом и интерпретатор примет Ваш вызов ни слова не сказав:
Функция ПриемПараметров1(чслПар1, чслПар2, чслПар3) Возврат чслПар1 + чслПар2 + чслПар3; КонецФункции Процедура Выполнить() Сообщить(""+ПриемПараметров1( , 3, )); КонецПроцедуры
Также существует нюанс при передаче параметров описанный в статье:
Занимательная передача параметров
//infostart.ru/profile/20743/blogs/628/
Поэтому, принимая параметры процедур и функций, стоит проверять их типы и приводить к нужным типам принудительно. Например:
Функция ПриемПараметров1(чслПар1, чслПар2, чслПар3) чслПар1=Число(чслПар1); чслПар2=Число(чслПар2); чслПар3=Число(чслПар3); Возврат чслПар1+чслПар2+чслПар3; КонецФункции
А для агрегатных типов:
Функция ПриемПараметров1(чслПар1, спрПар2) чслРез=1; // Результат: =1 – удачно, =0 – не удачно чслПар1=Число(чслПар1); Если ТипЗначения(спрПар2)<>11 Тогда // 11 – тип Справочник // Возможно еще вывод сообщения или другие действия чслРез=0; Возврат чслРез; Иначе Если спрПар2.Вид()<>"Сотрудники" Тогда // Возможно еще вывод сообщения или другие действия чслРез=0; Возврат чслРез; КонецЕсли; КонецЕсли; // // Операторы процедуры или функции // Возврат чслРез; КонецФункции
Следование этим не многочисленным правилам, позволит избежать многих ошибок и нежелательного и трудноотлавливаемого поведения программы еще на этапе разработки.