Рефлексивное программирование
В информатике рефлексивное программирование или рефлексия — это способность процесса исследовать , анализировать и изменять свою собственную структуру и поведение. [1]
Историческая справка [ править ]
Самые ранние компьютеры были запрограммированы на родных языках ассемблера , которые по своей сути были рефлексивными, поскольку эти оригинальные архитектуры можно было запрограммировать, определяя инструкции как данные и используя самомодифицирующийся код . Когда основная часть программирования перешла на компилируемые языки более высокого уровня , такие как Algol , Cobol , Fortran , Pascal и C , эта рефлексивная способность в значительной степени исчезла, пока не появились новые языки программирования со встроенной в их системы типов рефлексией. [ нужна цитата ]
Докторская диссертация Брайана Кэнтуэлла Смита 1982 года представила понятие вычислительного отражения в процедурных языках программирования и понятие метациклического интерпретатора как компонента 3-Lisp . [2] [3]
Использует [ править ]
Отражение помогает программистам создавать универсальные библиотеки программного обеспечения для отображения данных, обработки различных форматов данных, выполнения сериализации и десериализации данных для связи, а также объединения и разделения данных для контейнеров или пакетов связи.
Эффективное использование отражения почти всегда требует плана: структуры проектирования, описания кодировки, библиотеки объектов, карты базы данных или отношений сущностей.
Отражение делает язык более подходящим для сетевого кода. Например, он помогает таким языкам, как Java, хорошо работать в сетях, предоставляя библиотекам возможность сериализации, объединения и изменения форматов данных. Языки без отражения, такие как C, должны использовать вспомогательные компиляторы для таких задач, как абстрактная синтаксическая нотация, для создания кода для сериализации и объединения.
Отражение можно использовать для наблюдения и изменения выполнения программы во время выполнения . Программный компонент, ориентированный на отражение, может отслеживать выполнение части кода и изменять себя в соответствии с желаемой целью этой части. Обычно это достигается путем динамического назначения программного кода во время выполнения.
В объектно-ориентированных языках программирования, таких как Java , отражение позволяет проверять классы, интерфейсы, поля и методы во время выполнения, не зная имен интерфейсов, полей и методов во время компиляции . Он также позволяет создавать экземпляры новых объектов и вызывать методы.
Отражение часто используется как часть тестирования программного обеспечения , например, для создания/создания экземпляров фиктивных объектов во время выполнения .
Рефлексия также является ключевой стратегией метапрограммирования .
В некоторых объектно-ориентированных языках программирования, таких как C# и Java , отражение может использоваться для обхода доступности членов правил . Для свойств C# этого можно добиться путем записи непосредственно в (обычно невидимое) резервное поле закрытого свойства. Также можно найти закрытые методы классов и типов и вызвать их вручную. Это работает как для внутренних файлов проекта, так и для внешних библиотек, таких как сборки .NET и архивы Java.
Реализация [ править ]
Язык, поддерживающий отражение, предоставляет ряд функций, доступных во время выполнения, которые в противном случае было бы трудно реализовать на языке более низкого уровня. Некоторые из этих функций — это способности:
- Обнаруживайте и изменяйте конструкции исходного кода (такие как блоки кода, классы , методы, протоколы и т. д.) как первоклассные объекты во время выполнения .
- Преобразуйте строку , соответствующую символическому имени класса или функции, в ссылку или вызов этого класса или функции.
- Оценивайте строку так, как если бы она была оператором исходного кода во время выполнения.
- Создайте новый интерпретатор языка, байт-кода чтобы придать новое значение или назначение программной конструкции.
Эти функции могут быть реализованы разными способами. В MOO отражение является естественной частью повседневного программирования. При вызове глаголов (методов) заполняются различные переменные, такие как verb (имя вызываемого глагола) и this (объект, для которого вызывается глагол), чтобы задать контекст вызова. Безопасность обычно обеспечивается программным доступом к стеку вызывающих вызовов: поскольку callers () представляет собой список методов, с помощью которых в конечном итоге был вызван текущий глагол, выполнение тестов на вызывающих ()[0] (команда, вызванная исходным пользователем) позволяет глагол защитить себя от несанкционированного использования.
Скомпилированные языки полагаются на свою систему времени выполнения для предоставления информации об исходном коде. Например, скомпилированный исполняемый файл Objective-C записывает имена всех методов в блок исполняемого файла, предоставляя таблицу, соответствующую им базовым методам (или селекторам для этих методов), скомпилированным в программу. В компилируемом языке, который поддерживает создание функций во время выполнения, например Common Lisp , среда выполнения должна включать в себя компилятор или интерпретатор.
Отражение можно реализовать для языков без встроенного отражения, используя систему преобразования программ для определения автоматических изменений исходного кода.
Соображения безопасности [ править ]
Отражение может позволить пользователю создавать неожиданные пути потока управления через приложение, потенциально в обход мер безопасности. Этим могут воспользоваться злоумышленники. [4] Исторические уязвимости в Java, вызванные небезопасным отражением, позволяли коду, полученному с потенциально ненадежных удаленных компьютеров, выйти из изолированной программной среды механизма безопасности Java. Крупномасштабное исследование 120 уязвимостей Java, проведенное в 2013 году, пришло к выводу, что небезопасное отражение является наиболее распространенной уязвимостью в Java, хотя и не самой часто используемой. [5]
Примеры [ править ]
Следующие фрагменты кода создают экземпляр foo
класса Foo
и вызвать его метод PrintHello
. Для каждого языка программирования показаны обычные последовательности вызовов и последовательности вызовов, основанные на отражении.
Общий Лисп [ править ]
пример Ниже приведен использования Common Lisp Object System :
( defclass foo () ())
( defmethod print-hello (( f foo )) ( format T "Привет от ~S~%" f ))
;; Нормально, без отражения
( let (( foo ( make-instance 'foo )))
( print-hello foo ))
;; С отражением для поиска класса с именем «foo» и метода
;; с именем «print-hello», специализирующимся на «foo».
( let* (( foo-class ( find-class ( read-from-string "foo" )))
( print-hello-method ( find-method ( symbol-function ( read-from-string "print-hello" ) )
nil ( список foo-класса ))))
( funcall ( sb-mop:method-generic-function print-hello-method )
( make-instance foo-class )))
С# [ править ]
Ниже приведен пример на C# :
// Без отражения
var foo = new Foo ();
фу . РаспечататьПривет ();
// С отражением
Object foo = Activator . CreateInstance ( "complete.classpath.and.Foo" );
MethodInfo метод = foo . ПолучитьТип (). GetMethod ( "PrintHello" );
метод . Вызвать ( фу , ноль );
Delphi, Object Pascal [ править ]
В этом примере Delphi и Object Pascal предполагается, что Класс TFoo был объявлен в модуле под названием Раздел 1 :
использует RTTI , Unit1 ;
процедура БезОтражения ;
вар
Фу : TFoo ;
начать
Фу := TFoo . Создавать ;
попробуй
Фу . Привет ;
наконец
Фу . Бесплатно ;
конец ;
конец ;
процедура WithReflection ;
вар
RttiContext : TRttiContext ;
RttiType : TRttiInstanceType ;
Фу : ТОбъект ;
начать
RttiType := RttiContext . FindType ( 'Unit1.TFoo' ) как TRttiInstanceType ;
Фу := RttiType . GetMethod ( 'Создать' ) . Вызов ( RttiType . MetaclassType , []) . КакОбъект ;
попробуйте
РттиТип . GetMethod ( 'Привет' ) . Вызвать ( Foo , []) ;
наконец
Фу . Бесплатно ;
конец ;
конец ;
ЕС [ править ]
Ниже приведен пример в eC :
// Без отражения
Foo foo { };
фу . привет ();
// С отражением
Класс fooClass = eSystem_FindClass ( __thisModule , "Foo" );
Экземпляр foo = eInstance_New ( fooClass );
Метод m = eClass_FindMethod ( fooClass , » , fooClass.module « привет );
(( void ( * )())( void * ) m . function )( foo );
Иди [ править ]
Ниже приведен пример в Go :
import "reflect"
// Без отражения
f := Foo {}
f . Привет ()
// С отражением
fT := отражения . TypeOf ( Foo {})
fV := отражения . Новый ( fT )
m := fV . MethodByName ( «Привет» ),
если m . IsValid () {
м . Звонок ( ноль )
}
Ява [ править ]
Ниже приведен пример на Java :
импортировать java.lang.reflect.Method ;
// Без отражения
Foo foo = new Foo ();
фу . привет ();
// С отражением
try {
Object foo = Foo . сорт . getDeclaredConstructor (). новыйэкземпляр ();
Метод m = foo . ПолучитьКласс (). getDeclaredMethod ( "привет" , новый класс <?>[ 0 ] );
м . вызвать ( фу );
} catch ( ReflectiveOperationException игнорируется ) {}
JavaScript [ править ]
Ниже приведен пример на JavaScript :
// Без отражения
const foo = new Foo ()
foo . hello ()
// С отражением
const foo = Reflect . конструкция ( Foo )
const hello = Reflect . get ( foo , 'привет' )
Reflect . apply ( hello , foo , [])
// С eval
eval ( 'new Foo().hello()' )
Julia[editДжулия
Ниже приведен пример в Julia :
julia> struct Point
x :: Int
y
end
# Проверка с отражением
julia> fieldnames ( Point )
(:x, :y)
julia> fieldtypes ( Point )
(Int64, Any)
julia> p = Point ( 3 , 4 )
# Access с отражением
julia> getfield ( p , :x )
3
Objective-C [ править ]
Ниже приведен пример в Objective-C , подразумевающий, что OpenStep или Foundation Kit используется платформа :
// Класс Фу.
@interface Foo : NSObject
- ( void ) привет ;
@end
// Отправка «привет» экземпляру Foo без отражения.
Foo * obj = [[ Foo alloc ] init ];
[ объект привет ];
// Отправка «привет» экземпляру Foo с отражением.
id obj = [[ NSClassFromString ( @"Foo" ) alloc ] init ];
[ obj PerformSelector : @selector ( привет )];
Перл [ править ]
Ниже приведен пример на Perl :
# Без размышлений
my $foo = Foo -> new ;
$foo -> привет ;
# или
Foo -> new -> hello ;
# С отражением
my $class = "Foo"
my $constructor = "new" ;
мой $method = "привет" ;
мой $f = $class -> $constructor ;
$f -> $метод ;
# или
$класс -> $конструктор -> $метод ;
# с eval
eval "new Foo->hello;" ;
PHP [ править ]
Ниже приведен пример на PHP : [6]
// Без отражения
$foo = new Foo ();
$foo -> привет ();
// С отражением, используя Reflections API
$reflector = new ReflectionClass ( "Foo" );
$foo = $reflector -> newInstance ();
$hello = $reflector -> getMethod ( "привет" );
$hello -> вызвать ( $foo );
Питон [ править ]
Ниже приведен пример на Python :
# Без отражения
obj = Foo ()
obj . hello ()
# С отражением
obj = globals ()[ "Foo" ]()
getattr ( obj , "hello" )()
# С eval
eval ( "Foo().hello()" )
Р [ править ]
Ниже приведен пример в R :
# Без отражения, предполагая, что foo() возвращает объект типа S3, имеющий метод "hello"
obj <- foo ()
hello ( obj )
# С отражением
имя_класса <- "foo"
generic_having_foo_method <- "hello"
obj <- do. вызов ( имя_класса , список ())
do.call ( generic_having_foo_method , alist ( obj ))
Руби [ править ]
Ниже приведен пример в Ruby :
# Без отражения
obj = Foo . новый
объект . привет
# С отражением
obj = Object . const_get ( "Фу" ) . новый
объект . send :hello
# С eval
eval "Foo.new.hello"
Содзё [ править ]
Ниже приведен пример использования Xojo :
'Без отражения
Dim fooInstance As New Foo
fooInstance . PrintHello
' С отражением
Dim classInfo As Introspection . Typeinfo = GetTypeInfo ( Foo )
Dim Конструкторы () As Introspection . Информация о конструкторе = Информация о классе . GetConstructors
Dim fooInstance As Foo = конструкторы ( 0 ). Вызов
Dim методов () как интроспекция . Информация о методе = Информация о классе . GetMethods
For Each m как самоанализ . MethodInfo В методах
If m . Name = «PrintHello» Тогда
m . Вызов ( fooInstance )
End If
Next
См. также [ править ]
- Список рефлексивных языков и платформ программирования
- Зеркало (программирование)
- Парадигмы программирования
- Самостоятельный хостинг (компиляторы)
- Самомодифицирующийся код
- Тип самоанализ
- тип
Ссылки [ править ]
Цитаты [ править ]
- ^ Учебное пособие по поведенческой рефлексии и ее реализации Жака Маленфана и др. (PDF) , неизвестно, заархивировано из оригинала (PDF) 21 августа 2017 г. , получено 23 июня 2019 г.
- ^ Брайан Кантвелл Смит, Процедурная рефлексия в языках программирования , факультет электротехники и информатики, Массачусетский технологический институт, докторская диссертация, 1982.
- ^ Брайан С. Смит. Рефлексия и семантика в процедурном языке. Архивировано 13 декабря 2015 г. на Wayback Machine . Технический отчет MIT-LCS-TR-272, Массачусетский технологический институт, Кембридж, Массачусетс, январь 1982 г.
- ^ Баррос, Пауло; Просто, Рене; Мильштейн, Сюзанна; Вайнс, Пол; Дитль, Вернер; д'Аморим, Марсело; Эрнст, Майкл Д. (август 2015 г.). Статический анализ неявного потока управления: разрешение отражений Java и намерений Android (PDF) (отчет). Университет Вашингтона. UW-CSE-15-08-01 . Проверено 7 октября 2021 г.
- ^ Эувидум, Иеу; дисковый шум (5 октября 2021 г.). «Двадцать лет побега из песочницы Java» . Фрак . Том. 10, нет. 46 . Проверено 7 октября 2021 г.
- ^ «PHP: ReflectionClass — Руководство» . www.php.net .
Источники [ править ]
- Джонатан М. Собел и Дэниел П. Фридман. Введение в рефлексивно-ориентированное программирование (1996), Университет Индианы .
- Техника анти-отражения с использованием оболочки C# и C++/CLI для предотвращения кражи кода.
Дальнейшее чтение [ править ]
- Ира Р. Форман и Нейт Форман, Java Reflection в действии (2005), ISBN 1-932394-18-4
- Ира Р. Форман и Скотт Дэнфорт, Использование метаклассов в работе (1999), ISBN 0-201-43305-2