反射式编程

计算机程序检查其程序本身的能力

电脑学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指电脑程序运行时runtime)可以访问、检测和修改它本身状态或行为的一种能力。[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

要注意术语“反射”和“内省”(type introspection)的关系。内省(或称“自省”)机制仅指程序在运行时对自身资讯(称为元数据)的检测;反射机制不仅包括要能在运行时对程序自身资讯进行检测,还要求程序能进一步根据这些资讯改变程序状态或结构。[1]

概况

反射用于观察并修改程序在运行时的行为。一个反射导向的程序组件可以监测一个范围内的代码执行情况,可以根据获取的目标对象资讯及与此相关的范围修改自身。这可通过在运行时动态分配程序代码实现。

在类型检测严格的面向对象编程语言Java中,一般需要在编译期间对程序中需要调用的对象的具体类型、接口(interface)、资料成员(fields)和方法的合法性进行检查。反射技术则允许将对需要调用的物件的消息检查工作从编译期间推迟到运行期间再现场执行。这样一来,可以在编译期间先不明确目标物件的接口(interface)名称、字段(fields),即物件的资料成员(成员变量)、可用方法,然后在运行根据目标物件自身的消息决定如何处理。它还允许根据判断结果进行实例化新物件和相关方法的调用。

反射主要用途就是使给定的程序,动态地适应不同的运行情况。利用面向对象建模中的多态(多态性)也可以简化编写分别适用于多种不同情形的功能代码,但是反射可以解决多态(多态性)并不适用的更普遍情形,从而更大程度地避免硬编码(即把代码的细节“写死”,缺乏灵活性)的代码风格。

反射也是元编程的一个关键策略。

历史背景

早期电脑的原生汇编语言本质上就具有反射特性。因为这些最初架构可以通过定义指令作为数据及使用自修改代码来编程,实现反射功能是很平常的。编程发展到使用编译型高级语言如AlgolCobolFortran和包括PascalC在内的很多其他语言时,自修改代码等实践很大程度上消失了,直到将反射特性内建入类型系统的高级编程语言出现后才再次提供了反射功能。Lisp语言家族以具有同像性作为标志性特征,可以认为具有反射性。

1982年,布莱恩·史密斯英语Brian Cantwell Smith在其博士论文《编程语言中的过程式反射》中[2],向过程式编程语言介入了“计算反射”的概念,并且引入自循环解释器概念用作3-Lisp的一个组成部分[3]

特点

优点

支持反射的语言提供了一些在早期高级语言中难以实现的运行时特性。

  • 可以在一定程度上避免硬编码,提供灵活性和通用性。[4]
  • 可以作为一个头等物件发现并修改原始码的结构(如代码块、类、方法、协议等)。
  • 可以在运行时像对待原始码语句一样动态解析字符串中可执行的代码(类似JavaScript的eval()函数),进而可将跟class或function匹配的字符串转换成class或function的调用或引用。
  • 可以创建一个新的语言字节码解释器来给编程结构一个新的意义或用途。

劣势

  • 此技术的学习成本高。面向反射的编程需要较多的高级知识,包括框架、关系映射和对象交互,以实现更通用的代码执行。
  • 同样因为反射的概念和语法都比较抽象,过多地滥用反射技术会使得代码难以被其他人读懂,不利于合作与交流。[4]
  • 由于将部分资讯检查工作从编译期推迟到了运行期,此举在提高了代码灵活性的同时,牺牲了一点点运行效率。[4]

通过深入学习反射的特性和技巧,它的劣势可以尽量避免,但这需要许多时间和经验的积累。[4]

例子

下列代码片段建立Foo的一个实例foo,并调用它的方法PrintHello。对于每个编程语言,展示平常的和基于反射的调用序列。

C#

// Without reflection
Foo foo = new Foo();
foo.PrintHello();

// With reflection
System.Object foo2 = System.Activator.CreateInstance(System.Type.GetType("complete.classpath.and.Foo"));
System.Reflection.MethodInfo method = foo2.GetType().GetMethod("PrintHello");
method.Invoke(foo2, null);

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 {
    // Alternatively: Object foo = Foo.class.newInstance();
    Object foo = Class.forName("complete.classpath.and.Foo").newInstance();

    Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
    m.invoke(foo);
} catch (Exception e) {
    // Catching ClassNotFoundException, NoSuchMethodException
    // InstantiationException, IllegalAccessException
}

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

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

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

# With reflection
obj = globals()['Foo']() # globals() Return a dictionary representing the current global symbol table. 
getattr(obj, 'hello')()  # getattr(object, name) Return the value of the named attribute of object.  

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

R

# Without reflection, assuming foo() returns an S3-type object that has method "hello"
obj <- foo()
hello(obj)

# With reflection
the.class <- "foo"
the.method <- "hello"
obj <- do.call(the.class, list())
do.call(the.method, alist(obj))

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"

常见应用

  • 反射经常作为软件测试的一部分,比如运行时创建/实例化模拟对象。
  • Java语言解析XML文件的技术用到了反射。

参见

参考资料

引用

  1. ^ 1.0 1.1 Forman 2005,第8页。
  2. ^ Brian Cantwell Smith, Procedural Reflection in Programming Languages, Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD dissertation, 1982.
  3. ^ 3-lisp: an infinite tower of meta-circular interpreters. [2023-01-26]. (原始内容存档于2023-01-26). 
  4. ^ 4.0 4.1 4.2 4.3 Forman 2005,第4页。

来源

  • Ira R. Forman, Nate Forman. Java Reflection in Action [Java反射实战] 1. Manning Publications Co. 2005年. ISBN 1-932394-18-4 (英语). 

外部链接