反思性编程
在计算机科学中,反思性编程或反思是过程检查,内省和修改其自身的结构和行为的能力。
历史背景
最早的计算机是在其本机汇编语言中编程的,它们本质上是反射性的,因为这些原始体系结构可以通过将指令定义为数据并使用自修改代码来编程。随着大部分编程移至高级编译的语言,例如Algol , Cobol , Fortran , Pascal和C ,这种反射能力在很大程度上消失了,直到出现了带有反射的新编程语言,其类型系统内置了反射。
布莱恩·坎特威尔·史密斯(Brian Cantwell Smith)的1982年博士学位论文介绍了程序编程语言中计算反思的概念,而元圆形解释器作为3-LISP的组成部分。
用途
反思可帮助程序员制作通用软件库来显示数据,处理不同的数据格式,执行序列化和进行通信的数据序列化,或者捆绑和捆绑数据以用于容器或通信爆发。
有效使用反射几乎总是需要一个计划:设计框架,编码描述,对像库,数据库或实体关系的地图。
反思使一种语言更适合于面向网络的代码。例如,它可以通过启用库来序列化,捆绑和变化的数据格式来帮助Java等语言在网络中运行良好。没有反思的语言(例如C)必须使用辅助编译器来进行抽象语法符号等任务来生成序列化和捆绑的代码。
反射可用于观察和修改运行时的程序执行。面向反射的程序组件可以监视代码外壳的执行,并可以根据该机箱的所需目标进行修改。这通常是通过在运行时动态分配程序代码来完成的。
在以对象为导向的编程语言(例如Java)中,反射允许在运行时检查类,接口,字段和方法,而无需在编译时知道接口,字段,方法的名称。它还允许对新对象进行实例化和方法调用。
反射通常用作软件测试的一部分,例如模拟对象的运行时创建/实例化。
反射也是元编程的关键策略。
在某些面向对象的编程语言(例如C#和Java)中,反射可用于绕过会员可访问性规则。对于c# - properties,这可以通过直接写入非公共属性的(通常是看不见的)背衬字段来实现。也可以找到非公共的类和类型的方法,并手动调用它们。这适用于项目内部文件以及外部库,例如.NET的汇编和Java的档案。
执行
语言支持反射提供了许多在运行时可用的功能,否则在低级语言中很难完成。其中一些功能是:
- 在运行时发现并修改源代码构造(例如代码块,类,方法,协议等)为一流对象。
- 将匹配类或函数符号名称匹配的字符串转换为对该类或功能的引用或调用。
- 评估字符串,就好像它是运行时的源代码语句一样。
- 为语言的字节码创建新的解释器,以赋予编程结构的新含义或目的。
这些功能可以通过不同的方式实现。在MOO中,反射构成了日常编程成语的自然部分。当调用动词(方法)时,各种变量,例如动词(称为动词的名称),并且该(称为动词的对象)被填充以给出呼叫的上下文。安全性通常是通过编程访问呼叫者堆栈来管理的:既然Callers ()是最终调用当前动词的方法列表,请在Callers ()[0]上执行测试(原始用户调用的命令)允许该测试动词以保护自己免受未经授权的使用。
编译的语言依靠其运行时系统来提供有关源代码的信息。例如,编制的Objective-C可执行文件记录了可执行文件中的所有方法的名称,该表提供了一个表格,将这些方法与编译到程序中的基础方法(或这些方法的选择器)相对应。在支持运行时创建函数(例如Common LISP)的编译语言中,运行时环境必须包括编译器或解释器。
可以通过使用程序转换系统来定义自动源代码更改,无需内置反射而无需内置反射。
安全考虑
反射可能允许用户通过应用程序创建意外的控制流道路,并可能绕过安全措施。这可能是由攻击者利用的。由不安全反射引起的Java中的历史漏洞允许从潜在不受信任的远程机器中检索到的代码,从而突破了Java沙盒安全机制。 2013年对120个Java漏洞的一项大规模研究得出的结论是,不安全的反射是Java中最常见的脆弱性,尽管不是最被剥削的漏洞。
例子
以下代码片段创建一个实例foo
上课Foo
并调用其方法PrintHello
。对于每种编程语言,显示了基于正常和反射的呼叫序列。
C#
以下是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 /对象pascal
这个delphi / object pascal示例假设tfoo类已在一个名为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
以下是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
Objective-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中的一个示例:
// 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
以下是Python中的一个例子:
# Without reflection
obj = Foo()
obj.hello()
# With reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()
# With eval
eval("Foo().hello()")
R
以下是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
以下是使用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