Итак.
Все мы знаем, что в 1С можно написать такое:
Процедура ОбработатьОбъект(Знач Объект)
КонецПроцедуры
Т.е. тем самым мы передаем в процедуру параметр по значению.
Затем можем написать Эксперимент 1
Процедура ОбработатьОбъект(Знач Объект)
Объект = 3;
Сообщить(Объект);
КонецПроцедуры
А = 2;
Сообщить(А);
ОбработатьОбъект(А);
Сообщить(А);
И получим в итоге:
2
3
2
Далее можем сделать Эксперимент 2
Процедура ОбработатьОбъект(Знач Объект)
Объект.Очистить();
Сообщить(Объект.Количество());
КонецПроцедуры
А = Новый Массив;
А.Добавить(1);
А.Добавить(2);
А.Добавить(3);
А.Добавить(4);
Сообщить(А.Количество());
ОбработатьОбъект(А);
Сообщить(А.Количество());
И получим в итоге:
4
0
0
Но можем, наконец, провести Эксперимент 3:
Процедура ОбработатьОбъект(Знач Объект)
Объект = Новый Массив;
Сообщить(Объект.Количество());
КонецПроцедуры
А = Новый Массив;
А.Добавить(1);
А.Добавить(2);
А.Добавить(3);
А.Добавить(4);
Сообщить(А.Количество());
ОбработатьОбъект(А);
Сообщить(А.Количество());
И получим в итоге:
4
0
4
Выводы
Вывод из первого эксперимента простой – с примитивными типами использование Знач допустимо и работает ожидаемо.
Вывод из второго эксперимента, казалось бы, тоже простой – как минимум с коллекциями значений использование Знач НЕ работает или работает НЕ ожидаемо.
А вот вывод из третьего эксперимента гораздо интереснее – использование Знач с коллекциями значений (и прочими НЕ примитивными типами) работает, все-таки, ожидаемо!!!
Если кому-то интересны причины таких результатов – читайте дальше.
Теперь разберем механику
Для понимания механики обратимся сначала к трем более базовым темам программирования:
1) механика создания переменных (на уровне компилятора/интерпретатора)
2) работа с указателями на область памяти (я сознательно НЕ пишу выражение «ссылка на область памяти», чтобы не смешивать разные для 1С понятия в слове «ссылка»)
3) механика передачи параметров при вызове процедур и функций
Начнем.
Механика создания переменных
В процессе выполнения программы при создании и инициализации переменной система (компилятор или интерпретатор) производит следующее:
- заведение в таблице переменных новой строки для этой новой переменной
- резервирование некоего участка памяти
- занесение в одну из ячеек созданной строки адреса этого участка памяти
- заполнение выделенного участка памяти требуемым значением
Далее.
Работа с указателями на область памяти
Для примитивных типов это (механизм создания переменных) работает один в один.
Для НЕ примитивных типов это работает сложнее: в зарезервированный участок памяти помещается указатель на другой участок памяти, в котором уже и хранятся требуемые данные.
И наконец - Механика передачи параметров при вызове процедур и функций
Перед вызовом процедуры создается, так называемый, стек параметров, в который помещаются указатели на участки памяти с требуемыми значениями. (Если при вызове процедуры в качестве параметра указано выражение, то в стек помещается указатель на участок памяти, в который помещается результат вычисления выражения)
Если в описании процедуры параметр описан со словом Знач, то перед помещением в стек система (компилятор/интерпретатор) производит
- выделение нового участка памяти
- копирование в выделенный участок содержимого того участка памяти, на который показывает указатель из таблицы переменных
- помещение в стек указателя на выделенный участок памяти.
Тем самым – при возврате из процедуры содержимое старого участка памяти остается НЕТРОНУТЫМ.
Ну а далее все просто: во всех трех экспериментах при создании стека параметров произошло одно и то же - произошло копирование участка памяти со значением переменной А.
НО:
В эксперименте 1 скопировалось непосредственно значение 2.
В эксперименте 2 скопировался только указатель на ту область памяти, в которой был размещен массив.
Потому работа с массивом внутри процедуры привела к работе с самим массивом.
В эксперименте 3 произошло абсолютно то же самое, что и в эксперименте 2.
НО потом во вновь выделенный участок памяти был помещен указатель на вновь созданный массив!
Тем самым мы работали с новым массивом, а старый массив остался НЕТРОНУТЫМ.
Отсюда следует простой вывод – в 1С передача параметра по значению работает ожидаемо: копируется значение из участка памяти, на который указывает соответствующая ячейка таблицы переменных.
Т.е. для примитивных типов копируются непосредственно данные (т.е. само значение).
А вот для НЕ примитивных типов копируется лишь указатель на непосредственно данные.
Почему так
На мой взгляд, дело в том, что для полноценной передачи по значению НЕ примитивных типов необходимо реализовать сериализацию этого типа с последующей ДЕсериализацией.
А это:
1) затратно в плане времени исполнения и использования памяти (ведь коллекция может быть большой)
2) не все НЕпримитивные типы в 1С могут быть сериализованы и десериализованы.
Как с этим жить
Один из обходных вариантов
- перед вызовом процедуры делать ЗначениеВСтрокуВнутр()/сериализацию в XML/помещать в хранилище значения/проч.,
- передать в процедуру полученную строку,
- а в самой процедуре вызвать ЗначениеИзСтрокиВнутр()/десериализацию из XML/извлечение из хранилища/проч..
Но, повторюсь, - не со всеми типами это сработает!