Рефлексивное программирование
В информатике рефлексивное программирование или рефлексия — это способность процесса исследовать , анализировать и изменять свою собственную структуру и поведение. [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 "Hello from ~S~%" f))
;; Normal, without reflection
(let ((foo (make-instance 'foo)))
(print-hello foo))
;; With reflection to look up the class named "foo" and the method
;; named "print-hello" that specializes on "foo".
(let* ((foo-class (find-class (read-from-string "foo")))
(print-hello-method (find-method (symbol-function (read-from-string "print-hello"))
nil (list foo-class))))
(funcall (sb-mop:method-generic-function print-hello-method)
(make-instance foo-class)))
С#
[ редактировать ]Ниже приведен пример на C# :
// Without reflection
var foo = new Foo();
foo.PrintHello();
// With reflection
Object foo = Activator.CreateInstance("complete.classpath.and.Foo");
MethodInfo method = foo.GetType().GetMethod("PrintHello");
method.Invoke(foo, null);
Делфи, Объект Паскаль
[ редактировать ]В этом примере Delphi и Object Pascal предполагается, что Класс TFoo был объявлен в модуле под названием Блок1 :
uses RTTI, Unit1;
procedure WithoutReflection;
var
Foo: TFoo;
begin
Foo := TFoo.Create;
try
Foo.Hello;
finally
Foo.Free;
end;
end;
procedure WithReflection;
var
RttiContext: TRttiContext;
RttiType: TRttiInstanceType;
Foo: TObject;
begin
RttiType := RttiContext.FindType('Unit1.TFoo') as TRttiInstanceType;
Foo := RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsObject;
try
RttiType.GetMethod('Hello').Invoke(Foo, []);
finally
Foo.Free;
end;
end;
ЕС
[ редактировать ]Ниже приведен пример в eC:
// Without reflection
Foo foo { };
foo.hello();
// With reflection
Class fooClass = eSystem_FindClass(__thisModule, "Foo");
Instance foo = eInstance_New(fooClass);
Method m = eClass_FindMethod(fooClass, "hello", fooClass.module);
((void (*)())(void *)m.function)(foo);
Идти
[ редактировать ]Ниже приведен пример в Go :
import "reflect"
// Without reflection
f := Foo{}
f.Hello()
// With reflection
fT := reflect.TypeOf(Foo{})
fV := reflect.New(fT)
m := fV.MethodByName("Hello")
if m.IsValid() {
m.Call(nil)
}
Ява
[ редактировать ]Ниже приведен пример на Java :
import java.lang.reflect.Method;
// Without reflection
Foo foo = new Foo();
foo.hello();
// With reflection
try {
Object foo = Foo.class.getDeclaredConstructor().newInstance();
Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
m.invoke(foo);
} catch (ReflectiveOperationException ignored) {}
JavaScript
[ редактировать ]Ниже приведен пример на JavaScript :
// Without reflection
const foo = new Foo()
foo.hello()
// With reflection
const foo = Reflect.construct(Foo)
const hello = Reflect.get(foo, 'hello')
Reflect.apply(hello, foo, [])
// With eval
eval('new Foo().hello()')
Юлия
[ редактировать ]Ниже приведен пример в Julia :
julia> struct Point
x::Int
y
end
# Inspection with reflection
julia> fieldnames(Point)
(:x, :y)
julia> fieldtypes(Point)
(Int64, Any)
julia> p = Point(3,4)
# Access with reflection
julia> getfield(p, :x)
3
Цель-C
[ редактировать ]Ниже приведен пример в Objective-C , подразумевающий, что OpenStep , либо Foundation Kit используется либо платформа :
// Foo class.
@interface Foo : NSObject
- (void)hello;
@end
// Sending "hello" to a Foo instance without reflection.
Foo *obj = [[Foo alloc] init];
[obj hello];
// Sending "hello" to a Foo instance with reflection.
id obj = [[NSClassFromString(@"Foo") alloc] init];
[obj performSelector: @selector(hello)];
Перл
[ редактировать ]Ниже приведен пример на Perl :
# Without reflection
my $foo = Foo->new;
$foo->hello;
# or
Foo->new->hello;
# With reflection
my $class = "Foo"
my $constructor = "new";
my $method = "hello";
my $f = $class->$constructor;
$f->$method;
# or
$class->$constructor->$method;
# with eval
eval "new Foo->hello;";
PHP
[ редактировать ]Ниже приведен пример на PHP : [6]
// Without reflection
$foo = new Foo();
$foo->hello();
// With reflection, using Reflections API
$reflector = new ReflectionClass("Foo");
$foo = $reflector->newInstance();
$hello = $reflector->getMethod("hello");
$hello->invoke($foo);
Питон
[ редактировать ]Ниже приведен пример на Python :
# Without reflection
obj = Foo()
obj.hello()
# With reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()
# With eval
eval("Foo().hello()")
Р
[ редактировать ]Ниже приведен пример в R :
# Without reflection, assuming foo() returns an S3-type object that has method "hello"
obj <- foo()
hello(obj)
# With reflection
class_name <- "foo"
generic_having_foo_method <- "hello"
obj <- do.call(class_name, list())
do.call(generic_having_foo_method, alist(obj))
Руби
[ редактировать ]Ниже приведен пример в Ruby :
# Without reflection
obj = Foo.new
obj.hello
# With reflection
obj = Object.const_get("Foo").new
obj.send :hello
# With eval
eval "Foo.new.hello"
Хохо
[ редактировать ]Ниже приведен пример использования Xojo :
' Without reflection
Dim fooInstance As New Foo
fooInstance.PrintHello
' With reflection
Dim classInfo As Introspection.Typeinfo = GetTypeInfo(Foo)
Dim constructors() As Introspection.ConstructorInfo = classInfo.GetConstructors
Dim fooInstance As Foo = constructors(0).Invoke
Dim methods() As Introspection.MethodInfo = classInfo.GetMethods
For Each m As Introspection.MethodInfo In methods
If m.Name = "PrintHello" Then
m.Invoke(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