Reflekterende programmering - Reflective programming

Inden for datalogi er reflekterende programmering eller refleksion processens evne til at undersøge, introspektere og ændre sin egen struktur og adfærd.

Historisk baggrund

De tidligste computere blev programmeret på deres eget samlingssprog , som i sagens natur var reflekterende, da disse originale arkitekturer kunne programmeres ved at definere instruktioner som data og bruge selvmodificerende kode . Da hovedparten af ​​programmeringen flyttede til kompilerede sprog på højere niveau som Algol , Cobol , Fortran , Pascal og C , forsvandt denne reflekterende evne stort set, indtil nye programmeringssprog med refleksion indbygget i deres typesystemer dukkede op.

Brian Cantwell Smiths doktorafhandling fra 1982 introducerede begrebet beregningsreflektion i procedureprogrammerende sprog og forestillingen om meta-cirkulær tolk som en komponent i 3-Lisp .

Anvendelser

Refleksion hjælper programmerere med at lave generiske softwarebiblioteker til at vise data, behandle forskellige dataformater, udføre serialisering eller deserialisering af data til kommunikation eller bundtning og adskillelse af data til containere eller bursts af kommunikation.

Effektiv brug af refleksion kræver næsten altid en plan: En designramme, kodningsbeskrivelse, objektbibliotek, et kort over en database eller enhedsrelationer.

Refleksion gør et sprog mere egnet til netværksorienteret kode. For eksempel hjælper det sprog som Java med at fungere godt i netværk ved at aktivere biblioteker til serialisering, bundling og varierende dataformater. Sprog uden refleksion som C er forpligtet til at bruge hjælpekompilatorer til opgaver som Abstract Syntax Notation for at producere kode til serialisering og bundling.

Refleksion kan bruges til at observere og ændre programkørsel ved runtime . En refleksionsorienteret programkomponent kan overvåge udførelsen af ​​et kabinet af kode og kan modificere sig selv i henhold til et ønsket mål for dette kabinet. Dette opnås typisk ved dynamisk tildeling af programkode ved runtime.

I objektorienterede programmeringssprog som Java tillader refleksion inspektion af klasser, grænseflader, felter og metoder under kørsel uden at kende navnene på grænsefladerne, felterne, metoderne på kompileringstidspunktet. Det tillader også instantiering af nye objekter og påkaldelse af metoder.

Refleksion bruges ofte som en del af softwaretest , f.eks. Til runtime -oprettelse/instantiering af mock -objekter .

Refleksion er også en central strategi for metaprogrammering .

I nogle objektorienterede programmeringssprog som C# og Java kan refleksion bruges til at omgå regler for tilgængelighed for medlemmer . For C#-ejendomme kan dette opnås ved at skrive direkte på det (normalt usynlige) bakkefelt i en ikke-offentlig ejendom. Det er også muligt at finde ikke-offentlige metoder til klasser og typer og manuelt påberåbe dem. Dette fungerer for projektinterne filer samt eksterne biblioteker, f.eks. .NETs samlinger og Javas arkiver.

Implementering

Et sprog, der understøtter refleksion, giver en række funktioner tilgængelige under runtime, som ellers ville være vanskelige at opnå på et sprog på lavere niveau. Nogle af disse funktioner er evnerne til at:

  • Opdag og rediger kildekodekonstruktioner (f.eks. Kodeblokke, klasser , metoder, protokoller osv.) Som førsteklasses objekter under runtime .
  • Konverter en streng, der matcher det symbolske navn på en klasse eller funktion, til en henvisning til eller påkaldelse af den pågældende klasse eller funktion.
  • Evaluer en streng, som var den en kildekode-erklæring ved runtime.
  • Opret en ny tolk til sprogets bytecode for at give en ny mening eller et formål med en programmeringskonstruktion.

Disse funktioner kan implementeres på forskellige måder. I MOO udgør refleksion en naturlig del af dagligdags programmeringsform. Når verber (metoder) kaldes, udfyldes forskellige variabler som f.eks. Verb (navnet på verbet, der kaldes) og dette (objektet, som verbet kaldes på) for at give konteksten for opkaldet. Sikkerhed styres typisk ved at få adgang til opkaldsstakken programmatisk: Da opkaldere () er en liste over de metoder, hvormed det aktuelle verbum til sidst blev kaldt, gør udførelse af test på opkaldere () [0] (kommandoen påkaldt af den oprindelige bruger) det muligt for verb for at beskytte sig mod uautoriseret brug.

Kompilerede sprog er afhængige af deres runtime -system for at give oplysninger om kildekoden. En kompileret Objective-C- eksekverbar registrerer f.eks. Navnene på alle metoder i en blok af den eksekverbare fil og giver en tabel til at svare disse med de underliggende metoder (eller selektorer til disse metoder), der er samlet i programmet. I et kompileret sprog, der understøtter runtime -oprettelse af funktioner, f.eks. Common Lisp , skal runtime -miljøet indeholde en compiler eller en tolk.

Refleksion kan implementeres for sprog uden indbygget refleksion ved hjælp af et programtransformationssystem til at definere automatiserede kildekodeændringer.

Sikkerhedshensyn

Refleksion kan give en bruger mulighed for at oprette uventede kontrolstrømningsveje gennem et program, der muligvis kan omgå sikkerhedsforanstaltninger. Dette kan blive udnyttet af angribere. Historiske sårbarheder i Java forårsaget af usikker refleksion tillod kode hentet fra potentielt upålidelige eksterne maskiner til at bryde ud af Java -sandkassens sikkerhedsmekanisme. En stor undersøgelse af 120 Java -sårbarheder i 2013 konkluderede, at usikker refleksion er den mest almindelige sårbarhed i Java, men ikke den mest udnyttede.

Eksempler

Følgende kodestykker opretter en forekomst foo af klasse Foo og påberåber sig dens metode PrintHello . For hvert programmeringssprog vises normale og refleksionsbaserede opkaldssekvenser.

C#

Følgende er et eksempel i 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

Dette Delphi / Object Pascal -eksempel antager, at en TFoo -klasse er blevet erklæret i en enhed kaldet 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;

eC

Følgende er et eksempel i 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);

Følgende er et eksempel i 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

Følgende er et eksempel i Java :

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

Følgende er et eksempel i 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

Følgende er et eksempel i Julia (programmeringssprog) :

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

Mål-C

Følgende er et eksempel i Objective-C , hvilket indebærer, at enten OpenStep eller Foundation Kit- rammen bruges:

// 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

Følgende er et eksempel i 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

Følgende er et eksempel i 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);

Python

Følgende er et eksempel i Python :

# Without reflection
obj = Foo()
obj.hello()

# With reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()

# With eval
eval("Foo().hello()")

R

Følgende er et eksempel i 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

Følgende er et eksempel i 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

Følgende er et eksempel ved hjælp af 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

Se også

Referencer

Citater

Kilder

Yderligere læsning

eksterne links