Programowanie refleksyjne - Reflective programming
W informatyce , refleksyjny programowanie lub odbicie jest zdolność procesu do zbadania, introspekcji i modyfikować swoją własną strukturę i zachowanie.
Tło historyczne
Najwcześniejsze komputery były programowane w ich rodzimych językach asemblerowych , które były z natury refleksyjne, ponieważ te oryginalne architektury mogły być programowane poprzez zdefiniowanie instrukcji jako danych i użycie samomodyfikującego się kodu . Ponieważ większość programowania przeszła do języków skompilowanych wyższego poziomu , takich jak Algol , Cobol , Fortran , Pascal i C , ta zdolność refleksyjna w dużej mierze zniknęła, dopóki nie pojawiły się nowe języki programowania z refleksją wbudowaną w ich systemy typów.
Rozprawa doktorska Briana Cantwella Smitha z 1982 r. wprowadziła pojęcie refleksji obliczeniowej w proceduralnych językach programowania oraz pojęcie metakołowego interpretera jako składnika 3-Lisp .
Zastosowania
Odbicie pomaga programistom w tworzeniu ogólnych bibliotek oprogramowania do wyświetlania danych, przetwarzania różnych formatów danych, wykonywania serializacji lub deserializacji danych w celu komunikacji lub łączenia i rozdzielania danych dla kontenerów lub impulsów komunikacji.
Efektywne wykorzystanie refleksji prawie zawsze wymaga planu: ramy projektowej, opisu kodowania, biblioteki obiektów, mapy bazy danych lub relacji encji.
Refleksja sprawia, że język jest bardziej dostosowany do kodu zorientowanego na sieć. Na przykład pomaga językom takim jak Java, aby działały dobrze w sieciach, umożliwiając bibliotekom serializację, tworzenie pakietów i zmianę formatów danych. Języki bez refleksji, takie jak C, są wymagane do używania kompilatorów pomocniczych do zadań takich jak Notacja składni abstrakcyjnej w celu wytworzenia kodu do serializacji i łączenia w pakiety.
Odbicie może być używane do obserwowania i modyfikowania wykonywania programu w czasie wykonywania . Komponent programu zorientowany na refleksję może monitorować wykonanie załącznika kodu i może modyfikować się zgodnie z pożądanym celem tego załącznika. Jest to zazwyczaj realizowane przez dynamiczne przypisywanie kodu programu w czasie wykonywania.
W obiektowych językach programowania , takich jak Java , odbicie umożliwia inspekcję klas, interfejsów, pól i metod w czasie wykonywania bez znajomości nazw interfejsów, pól, metod w czasie kompilacji. Umożliwia także tworzenie instancji nowych obiektów i wywoływanie metod.
Refleksja jest często wykorzystywana jako część testowania oprogramowania , na przykład do tworzenia/instancji w środowisku wykonawczym makiet obiektów .
Refleksja jest również kluczową strategią metaprogramowania .
W niektórych zorientowanych obiektowo językach programowania, takich jak C# i Java , odbicie może służyć do ominięcia reguł dostępności elementów członkowskich . W przypadku właściwości C# można to osiągnąć, pisząc bezpośrednio na (zwykle niewidocznym) polu zapasowym właściwości niepublicznej. Możliwe jest również znalezienie niepublicznych metod klas i typów oraz ręczne ich wywoływanie. Działa to w przypadku plików wewnętrznych projektu, a także bibliotek zewnętrznych, takich jak zespoły .NET i archiwa Java.
Realizacja
Język obsługujący odbicie zapewnia szereg funkcji dostępnych w czasie wykonywania, które w innym przypadku byłyby trudne do osiągnięcia w języku niższego poziomu. Niektóre z tych funkcji to możliwości:
- Odkrywaj i modyfikuj konstrukcje kodu źródłowego (takie jak bloki kodu, klasy , metody, protokoły itp.) jako obiekty pierwszej klasy w czasie wykonywania .
- Konwertuj ciąg pasujący do nazwy symbolicznej klasy lub funkcji na odwołanie do tej klasy lub funkcji lub wywołanie tej klasy.
- Oceń ciąg tak, jakby był to instrukcja kodu źródłowego w czasie wykonywania.
- Utwórz nowy interpreter dla kodu bajtowego języka, aby nadać nowe znaczenie lub cel konstrukcji programistycznej.
Te funkcje mogą być realizowane na różne sposoby. W MOO refleksja stanowi naturalną część codziennego idiomu programowania. Kiedy wywoływane są czasowniki (metody), różne zmienne, takie jak czasownik (nazwa wywoływanego czasownika) i this (dopełnienie, w którym czasownik jest wywoływany), są wypełniane, aby podać kontekst wywołania. Bezpieczeństwo jest zwykle zarządzane poprzez programowy dostęp do stosu wywołujących: ponieważ wywołujący () to lista metod, za pomocą których został ostatecznie wywołany bieżący czasownik, wykonanie testów na wywołujących ()[0] (polecenie wywołane przez pierwotnego użytkownika) pozwala na czasownik do ochrony przed nieautoryzowanym użyciem.
Języki kompilowane polegają na swoim systemie wykonawczym w celu dostarczania informacji o kodzie źródłowym. Skompilowany plik wykonywalny Objective-C , na przykład, zapisuje nazwy wszystkich metod w bloku pliku wykonywalnego, dostarczając tabelę odpowiadającą tym metodom (lub selektorom dla tych metod) skompilowanym do programu. W skompilowanym języku obsługującym tworzenie funkcji w czasie wykonywania, takim jak Common Lisp , środowisko wykonawcze musi zawierać kompilator lub interpreter.
Odbicie można zaimplementować dla języków bez wbudowanego odbicia przy użyciu systemu transformacji programu do definiowania automatycznych zmian w kodzie źródłowym.
Względy bezpieczeństwa
Odbicie może umożliwić użytkownikowi tworzenie nieoczekiwanych ścieżek przepływu sterowania przez aplikację, potencjalnie z pominięciem środków bezpieczeństwa. Może to zostać wykorzystane przez atakujących. Historyczne luki w Javie spowodowane niebezpiecznym odbiciem umożliwiały kodowi pobranemu z potencjalnie niezaufanych zdalnych maszyn wydostanie się z mechanizmu bezpieczeństwa piaskownicy Java . Przeprowadzone w 2013 roku na dużą skalę badanie 120 luk w zabezpieczeniach Javy wykazało, że niebezpieczne odbicie jest najczęstszą luką w Javie, choć nie jest to najczęściej wykorzystywana.
Przykłady
Następujące fragmenty utworzyć przypadek foo
z klasy Foo
i wywołać jego sposobu PrintHello
. Dla każdego języka programowania pokazane są sekwencje wywołań normalnych i opartych na odbiciach.
C#
Poniżej znajduje się przykład w C# :
// Without reflection
Foo 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
Ten przykład Delphi / Object Pascal zakłada, że klasa TFoo została zadeklarowana w jednostce o nazwie Unit1 :
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;
eK
Poniżej znajduje się przykład w 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);
Udać się
Oto przykład w 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)
}
Jawa
Poniżej znajduje się przykład w Javie :
import java.lang.reflect.Method;
// Without reflection
Foo foo = new Foo();
foo.hello();
// With reflection
try {
Object foo = Foo.class.newInstance();
Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
m.invoke(foo);
} catch (ReflectiveOperationException ignored) {}
JavaScript
Poniżej znajduje się przykład w 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
Poniżej znajduje się przykład w Julia (język programowania) :
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
Cel C
Poniżej znajduje się przykład w Objective-C , sugerujący użycie frameworka OpenStep lub 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
Poniżej znajduje się przykład w Perlu :
# 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
Poniżej znajduje się przykład w PHP :
// 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);
Pyton
Poniżej znajduje się przykład w Pythonie :
# Without reflection
obj = Foo()
obj.hello()
# With reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()
# With eval
eval("Foo().hello()")
r
Poniżej znajduje się przykład w 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))
Rubin
Poniżej znajduje się przykład w Ruby :
# Without reflection
obj = Foo.new
obj.hello
# With reflection
class_name = "Foo"
method_name = :hello
obj = Object.const_get(class_name).new
obj.send method_name
# With eval
eval "Foo.new.hello"
Xojo
Poniżej znajduje się przykład użycia 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
Zobacz też
- Lista refleksyjnych języków programowania i platform
- Lustro (programowanie)
- Paradygmaty programowania
- Self-hosting (kompilatory)
- Samomodyfikujący się kod
- Wpisz introspekcję
- typ
Bibliografia
Cytaty
Źródła
- Jonathan M. Sobel i Daniel P. Friedman. Wprowadzenie do programowania zorientowanego na refleksję (1996), Indiana University .
- Technika Anti-Reflection przy użyciu wrappera C# i C++/CLI, aby zapobiec złodziejowi kodu
Dalsza lektura
- Ira R. Forman i Nate Forman, Java Odbicie w działaniu (2005), ISBN 1-932394-18-4
- Ira R. Forman i Scott Danforth, Wprowadzenie metaklas do pracy (1999), ISBN 0-201-43305-2