Рефлексія (програмування)
В інформатиці, рефлексія — це процес, під час якого комп'ютерна програма може відслідковувати і модифікувати власну структуру і поведінку під час виконання.[1]
Це, кажучи простими словами, спосіб звернення програми до власного коду.
Перші комп'ютери були запрограмовані за допомогою їхньої нативної мови асемблер. Рефлексивність у ній була спадковою, оскільки ранні комп'ютери могли бути запрограмовані через визначення виконуваних інструкцій як даних та з використанням самомодифікуючого коду. Коли програмування дійшло до високорівневих мов програмування, таких як C, рефлексивна здатність зникнула (за межами Шкідливого програмного засобу), поки не з'явилися мови програмування з вбудованою рефлексією в їхні системи типізації.
Поняття рефлексії в мовах програмування ввів Браян Кентвелл Сміт[en] в докторській дисертації 1982 р.[2][3]
Рефлексія може використовуватися для спостерігання і зміни програми під час виконання. Рефлексивний компонент програми може спостерігати за виконанням певної частини коду і змінювати себе для досягнення потрібної цілі. Модифікація виконується під час виконання програми шляхом динамічної зміни коду.
В об'єктно-орієнтованих мовах програмування, таких як Java, рефлексія дозволяє огляд класів, інтерфейсів, полів і методів в період виконання програми, не знаючи імен інтерфейсів, полів, методів в період компіляції. Вона також дозволяє викликати методи.
Рефлексію можна застосовувати для динамічної адаптації програми до різноманітних ситуацій. Наприклад, розгляньмо програму, яка використовує два різні класи X
і Y
для виконання аналогічних операцій. Без рефлексії в коді програми методи класів X
і Y
будуть викликатися явно. Якщо програма спроектована з використанням рефлексно-орієнтованої парадигми програмування, деяка ділянка коду не буде містити явних викликів методів класів X
і Y
; програма виконає цю частину двічі: спочатку для класу X
, потім для класу Y
.
Рефлексія — це один з видів метапрограмування.
В деяких об'єктно-орієнтованих мовах програмування, таких як C# і Java, рефлексію можна використовувати для перевизначення правил доступу до методів класу. Наприклад, за допомогою рефлексії можна змінити значення поля з модифікатором доступу private
з стороннього класу.
Програми, котрі написані за допомогою тих мов програмування, які підтримують рефлексію, мають додаткові можливості, реалізація яких на низькорівневих мовах є доволі важкою. Перерахуємо деякі з них:
- пошук і модифікація конструкцій початковий коду (блоків, класів, методів, протоколів і т.д.) як об'єктів першого класу під час виконання;
- зміна імен класів і функцій під час виконання;
- аналіз і виконання стрічок коду, які надходять;
- створення інтерпретатора псевдокоду нової мови.
Ці можливості можна реалізовувати різними способами. У мові MOO рефлексія є частиною щоденної ідіоми програмування. Всі методи, які викликаються, отримують в контексті інформацію про те, звідки вони були викликані, і посилання на об'єкти, до яких вони належать. Безпека контролюється програмно за допомогою стеку викликів: викликається callers() для отримання списку методів; перевіряється, чи не заблокував callers() сам себе.
Компільовані мови покладаються на свої системи виконання, які забезпечують програми інформацією про їхній початковий код. Скомпільований на Objective-C виконуваний файл, наприклад, записує імена всіх методів в один блок, створює таблицю відповідності. В компільованих мовах, які підтримують створення функцій під час виконання, таких як Common Lisp, середовище виконання має містити компілятор і інтерпретатор.
Реалізація рефлексії в мовах, які її не підтримують, виконується за допомогою системи трансформації програми для автоматичного відслідковування змін в початковому коді.
Наступні приклади показують застосування рефлексії на прикладі створення екземпляру foo
класу Foo
і виклику метода hello
(абоHello
) в різних мовах програмування. Для кожної мови наведено два приклади: у першому не використовується рефлексія, а у другому використовується.
Так само працює JavaScript і ActionScript:
//Без рефлексії
new Foo().hello()
// З рефлексією
// assuming that Foo resides in this
new this['Foo']()['hello']()
// or without assumption
new (eval('Foo'))()['hello']()
// Без рефлексії
new Foo().hello();
// З рефлексією
Class foo = Class.forName("Foo");
foo.getMethod("hello", null).invoke(foo.newInstance(), null);
Бібліотека Qt розширює можливості C++ за допомогою метамови й забезпечує підтримку рефлексії для посилання на члени/методи класу і запит імені об'єктів Qt за допомогою QMetaObject, що містить метадані про об'єкти Qt.
// Без рефлексії
QObject *obj = new QPushButton;
obj->metaObject()->className(); // "QPushButton"
// З рефлексією
QPushButton::staticMetaObject.className(); // "QPushButton"
-- Без рефлексії
Foo.hello()
-- З рефлексією
_G['Foo']['hello']()
// Без рефлексії
Foo *foo = [[Foo alloc] init];
[foo hello];
// З рефлексією
Class cls = NSClassFromString(@"Foo");
id foo = [[cls alloc] init];
SEL selector = NSSelectorFromString(@"hello");
[foo performSelector:selector withObject:nil];
# Без рефлексії
my $foo = Foo->new();
$foo->hello();
# З рефлексією (використовуючи синтаксис розіменування об'єктів)
my $class = "Foo";
my $method = "hello";
my $object = $class->new();
$object->$method();
//Без рефлексії
$oFoo = new Foo();
$oFoo->hello();
//З рефлексією (використовуючи ReflectionClass)
$oReflector = new ReflectionClass('Foo');
$oFoo = $oReflector->newInstance();
$oHello = $oReflector->getMethod('hello');
$oHello->invoke($oFoo);
//З рефлексією (використовуючи callback)
$oFoo = new Foo();
call_user_func(array($oFoo,'hello'));
//З рефлексією (використовуючи синтаксис розіменування об'єктів)
$class_name = "Foo";
$f = new $class_name();
$method = "hello";
$f->$method();
# Без рефлексії
Foo().hello()
# З рефлексією
getattr(globals()['Foo'](), 'hello')()
# Без рефлексії
Foo.new.hello
# З рефлексією
Object.const_get(:Foo).send(:new).send(:hello)
"Без рефлексії"
Foo new hello
"З рефлексією "
((Smalltalk at: #Foo) perform: #new) perform: #hello
Foo := Object clone do(
hello := method(
"Hello" println
)
)
# Без рефлексії
Foo hello
# З рефлексією
getSlot("Foo") getSlot("hello") call
// Без рефлексії
var foo:Foo = new Foo();
foo.hello();
// З рефлексією
var cls:Object = getDefinitionByName("Foo");
var foo:Object = new cls();
foo["hello"]();
// Без рефлексії
var
foo : TFoo;
begin
foo := TFoo.Create();
foo.Hello();
end;
// З рефлексією
var
c : TRttiContext;
t : TRttiInstanceType;
foo : TValue;
begin
c := TRttiContext.Create;
t := (c.FindType('TFoo') as TRttiInstanceType);
foo := t.GetMethod('Create').Invoke(t.MetaclassType,[]);
t.GetMethod('Hello').Invoke(foo,[]);
c.Free;
end.
- ↑ A Tutorial on Behavioral Reflection and its Implementation by Jacques Malenfant et al (PDF). Архів оригіналу (PDF) за 22 лютого 2016. Процитовано 2 червня 2015.
- ↑ Brian Cantwell Smith, Procedural Reflection in Programming Languages, Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD Thesis, 1982.
- ↑ Brian C. Smith. Reflection and semantics in a procedural language. Technical Report MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Mass., January 1982. Архів оригіналу за 13 грудня 2015. Процитовано 2 червня 2015.
- Forman, Ira R. and Forman, Nate. Java Reflection in Action. — Manning Publications Co, 2004. — ISBN 1932394184.
- Forman, Ira R. and Danforth, Scott H. Putting Metaclasses to Work: A New Dimension in Object-oriented Programming. — Addison Wesley Longman Publishing Co., Inc, 1999. — ISBN 0-201-43305-2.
- Програмні конструкції
- Статті з прикладами коду мовою Objective-C
- Статті з прикладами коду мовою C++
- Статті з прикладами коду мовою Smalltalk
- Статті з прикладами коду мовою ActionScript
- Статті з прикладами коду мовою PHP
- Статті з прикладами коду мовою Pascal
- Статті з прикладами коду мовою Lua
- Статті з прикладами коду мовою Java
- Статті з прикладами коду мовою Perl
- Статті з прикладами коду мовою JavaScript
- Статті з прикладами коду мовою Ruby