Введение

Способность процесса исследовать и изменять себя.

В информатике рефлексивное программирование, или рефлексия, — это способность процесса анализировать, интроспектировать и изменять свою собственную структуру и поведение.

Исторический фон

Самые ранние компьютеры программировались на их родных языках ассемблера, которые изначально были рефлексивными, поскольку в этих первых архитектурах программирование осуществлялось путем определения инструкций как данных и использования самомодифицирующегося кода. Когда большая часть программирования перешла на языки программирования высокого уровня с компиляцией, такие как Algol, Cobol, Fortran, Pascal и C, эта возможность рефлексии в значительной степени утратилась, пока не появились новые языки программирования с рефлексией, встроенной в их типовые системы. Докторская диссертация Брайана Кантуэлла Смита 1982 года представила понятие вычислительной рефлексии в процедурных языках программирования и понятие метациркулярного интерпретатора как компонента 3 Lisp.

Применение

Рефлексия помогает программистам создавать универсальные библиотеки программного обеспечения для отображения данных, обработки различных форматов данных, выполнения сериализации и десериализации данных для обмена, или для объединения и разделения данных в контейнеры или пакеты передачи. Эффективное использование рефлексии почти всегда требует планирования: наличие проектной структуры, описания кодирования, библиотеки объектов, схемы базы данных или связей между сущностями. Рефлексия делает язык более подходящим для разработки сетевого кода. Например, она помогает таким языкам, как Java, эффективно работать в сетях, предоставляя библиотеки для сериализации, объединения и обработки различных форматов данных. Языкам, не поддерживающим рефлексию, таким как C, требуются вспомогательные компиляторы для задач, таких как абстрактное синтаксическое представление, чтобы генерировать код для сериализации и объединения данных. Рефлексия может использоваться для наблюдения и изменения выполнения программы во время выполнения. Программный компонент, ориентированный на рефлексию, может отслеживать выполнение блока кода и изменять себя в соответствии с желаемой целью этого блока. Обычно это достигается путем динамического присвоения программного кода во время выполнения. В объектно-ориентированных языках программирования, таких как Java, рефлексия позволяет исследовать классы, интерфейсы, поля и методы во время выполнения, не зная их имен на этапе компиляции. Она также позволяет создавать новые экземпляры объектов и вызывать методы. Рефлексия часто используется в процессе тестирования программного обеспечения, например, для динамического создания/инстанцирования имитирующих объектов (mock objects). Рефлексия также является ключевой стратегией для метапрограммирования. В некоторых объектно-ориентированных языках программирования, таких как C# и Java, рефлексия может использоваться для обхода правил доступа к членам. Для свойств C# это можно реализовать, записывая непосредственно в (обычно невидимое) базовое поле непубличного свойства. Также возможно находить непубличные методы классов и типов и вызывать их вручную. Это работает как для внутренних файлов проекта, так и для внешних библиотек, таких как сборки .NET и архивы Java.

Реализация

Язык с поддержкой рефлексии предоставляет ряд возможностей, доступных во время выполнения, которые было бы сложно реализовать на языке более низкого уровня. Некоторые из этих возможностей включают в себя: обнаружение и изменение конструкций исходного кода (таких как блоки кода, классы, методы, протоколы и т. д.) как объектов первого класса во время выполнения; преобразование строки, соответствующей символическому имени класса или функции, в ссылку на этот класс или функцию, либо в вызов этой функции; вычисление строки как оператора исходного кода во время выполнения; создание нового интерпретатора для байт-кода языка, чтобы придать конструкции программирования новый смысл или назначение. Эти возможности могут быть реализованы различными способами. В MOO рефлексия является естественной частью повседневной практики программирования. При вызове глаголов (методов) различные переменные, такие как `verb` (имя вызываемого глагола) и `this` (объект, над которым вызывается глагол), заполняются, предоставляя контекст вызова. Безопасность обычно обеспечивается программным доступом к стеку вызовов: поскольку `callers` представляет собой список методов, через которые был вызван текущий глагол, проверка `callers[0]` (команда, введенная исходным пользователем) позволяет глаголу защититься от несанкционированного использования. Компилируемые языки полагаются на свою среду выполнения для предоставления информации об исходном коде. Например, скомпилированный исполняемый файл Objective-C сохраняет имена всех методов в блоке исполняемого файла, предоставляя таблицу для сопоставления этих имен с базовыми методами (или селекторами этих методов), скомпилированными в программу. В компилируемом языке, поддерживающем создание функций во время выполнения, таком как Common Lisp, среда выполнения должна включать компилятор или интерпретатор. Рефлексия может быть реализована для языков, не имеющих встроенной поддержки рефлексии, с помощью системы преобразования программ для определения автоматических изменений исходного кода.

Относительно безопасности

Отражение может позволить пользователю создавать неожиданные пути управления потоком выполнения в приложении, потенциально обходя меры безопасности. Это может быть использовано злоумышленниками. Исторические уязвимости в Java, вызванные небезопасным использованием рефлексии, позволяли коду, полученному с потенциально недоверенных удаленных машин, выходить за пределы механизма безопасности "песочницы" Java. Крупномасштабное исследование 120 уязвимостей Java, проведенное в 2013 году, показало, что небезопасная рефлексия является наиболее распространенной уязвимостью в Java, хотя и не самой часто эксплуатируемой.

Примеры

Следующие фрагменты кода создают экземпляр класса и вызывают его метод. Для каждого языка программирования показаны последовательности вызовов с использованием обычного вызова и отражения.