Programação reflexiva - Reflective programming

Na ciência da computação , a programação reflexiva ou reflexão é a capacidade de um processo de examinar, introspectar e modificar sua própria estrutura e comportamento.

Contexto histórico

Os primeiros computadores foram programados em suas linguagens de montagem nativas , que eram inerentemente reflexivas, uma vez que essas arquiteturas originais podiam ser programadas definindo instruções como dados e usando código de automodificação . À medida que a maior parte da programação mudou para linguagens compiladas de nível superior , como Algol , Cobol , Fortran , Pascal e C , essa capacidade reflexiva desapareceu em grande parte até que surgiram novas linguagens de programação com reflexão embutida em seus sistemas de tipos.

A tese de doutorado de 1982 de Brian Cantwell Smith introduziu a noção de reflexão computacional em linguagens de programação procedural e a noção do interpretador metacircular como um componente de 3-Lisp .

Usos

O Reflection ajuda os programadores a criar bibliotecas de software genéricas para exibir dados, processar diferentes formatos de dados, realizar serialização ou desserialização de dados para comunicação ou agrupar e desmembrar dados para contêineres ou rajadas de comunicação.

O uso eficaz da reflexão quase sempre requer um plano: uma estrutura de design, descrição de codificação, biblioteca de objetos, um mapa de um banco de dados ou relações entre entidades.

A reflexão torna a linguagem mais adequada ao código orientado para rede. Por exemplo, ajuda linguagens como Java a operar bem em redes, habilitando bibliotecas para serialização, empacotamento e formatos de dados variados. Linguagens sem reflexão, como C, são necessárias para usar compiladores auxiliares para tarefas como Abstract Syntax Notation para produzir código para serialização e empacotamento.

O Reflection pode ser usado para observar e modificar a execução do programa em tempo de execução . Um componente de programa orientado para reflexão pode monitorar a execução de um invólucro de código e pode se modificar de acordo com um objetivo desejado desse invólucro. Normalmente, isso é feito atribuindo dinamicamente o código do programa no tempo de execução.

Em linguagens de programação orientadas a objetos , como Java , a reflexão permite a inspeção de classes, interfaces, campos e métodos em tempo de execução sem saber os nomes das interfaces, campos, métodos em tempo de compilação. Também permite a instanciação de novos objetos e a invocação de métodos.

A reflexão é freqüentemente usada como parte do teste de software , como para a criação / instanciação de objetos simulados em tempo de execução .

A reflexão também é uma estratégia chave para a metaprogramação .

Em algumas linguagens de programação orientadas a objetos, como C # e Java , a reflexão pode ser usada para contornar as regras de acessibilidade dos membros . Para propriedades em C #, isso pode ser obtido gravando diretamente no campo de apoio (geralmente invisível) de uma propriedade não pública. Também é possível encontrar métodos não públicos de classes e tipos e invocá-los manualmente. Isso funciona para arquivos internos do projeto, bem como bibliotecas externas, como assemblies .NET e arquivos Java.

Implementação

Uma reflexão de suporte de linguagem fornece vários recursos disponíveis em tempo de execução que, de outra forma, seriam difíceis de realizar em uma linguagem de nível inferior. Alguns desses recursos são a capacidade de:

  • Descubra e modifique construções de código-fonte (como blocos de código, classes , métodos, protocolos, etc.) como objetos de primeira classe em tempo de execução .
  • Converta uma string que corresponda ao nome simbólico de uma classe ou função em uma referência ou invocação dessa classe ou função.
  • Avalie uma string como se fosse uma instrução de código-fonte em tempo de execução.
  • Crie um novo interpretador para o bytecode da linguagem para dar um novo significado ou propósito para uma construção de programação.

Esses recursos podem ser implementados de maneiras diferentes. No MOO , a reflexão é uma parte natural da linguagem de programação cotidiana. Quando verbos (métodos) são chamados, várias variáveis ​​como verbo (o nome do verbo sendo chamado) e this (o objeto no qual o verbo é chamado) são preenchidas para fornecer o contexto da chamada. A segurança é normalmente gerenciada acessando a pilha de chamadas programaticamente: Uma vez que callers () é uma lista dos métodos pelos quais o verbo atual foi eventualmente chamado, realizar testes em callers () [0] (o comando invocado pelo usuário original) permite que verbo para se proteger contra o uso não autorizado.

Linguagens compiladas contam com seu sistema de tempo de execução para fornecer informações sobre o código-fonte. Um executável Objective-C compilado , por exemplo, registra os nomes de todos os métodos em um bloco do executável, fornecendo uma tabela para corresponder aos métodos subjacentes (ou seletores para esses métodos) compilados no programa. Em uma linguagem compilada que suporta a criação de funções em tempo de execução, como Common Lisp , o ambiente de tempo de execução deve incluir um compilador ou um interpretador.

A reflexão pode ser implementada para linguagens sem reflexão embutida usando um sistema de transformação de programa para definir mudanças automatizadas de código-fonte.

Considerações de segurança

A reflexão pode permitir que um usuário crie caminhos de fluxo de controle inesperados através de um aplicativo, potencialmente contornando as medidas de segurança. Isso pode ser explorado por invasores. Vulnerabilidades históricas em Java causadas por reflexão insegura permitiram que o código recuperado de máquinas remotas potencialmente não confiáveis escapasse do mecanismo de segurança da sandbox do Java . Um estudo em grande escala de 120 vulnerabilidades do Java em 2013 concluiu que a reflexão insegura é a vulnerabilidade mais comum no Java, embora não seja a mais explorada.

Exemplos

Os fragmentos de código a seguir criam uma instância foo de classe Foo e invocam seu método PrintHello . Para cada linguagem de programação , sequências de chamadas normais e baseadas em reflexão são mostradas.

C #

A seguir está um exemplo em 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

Este exemplo Delphi / Object Pascal assume que uma classe TFoo foi declarada em uma unidade chamada 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

O seguinte é um exemplo em 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);

Ir

A seguir está um exemplo em 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

A seguir está um exemplo em 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

A seguir está um exemplo em 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

A seguir está um exemplo em Julia (linguagem de programação) :

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

Objective-C

A seguir está um exemplo em Objective-C , implicando que o framework OpenStep ou Foundation Kit é usado:

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

A seguir está um exemplo em 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

A seguir está um exemplo em 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);

Pitão

A seguir está um exemplo em Python :

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

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

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

R

A seguir está um exemplo em 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))

Rubi

A seguir está um exemplo em 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

A seguir está um exemplo usando 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

Veja também

Referências

Citações

Fontes

Leitura adicional

links externos