跳至內容

反射式編程

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書

電腦學中,反射式編程(英語: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 (英語). 

外部連結