リフレクション (情報工学)

情報工学においてリフレクション  とは、プログラムの実行過程でプログラム自身の構造を読み取ったり書き換えたりする技術のことを指す。

1. 概要
-----

日本語では自己言及と呼ばれる。通常リフレクションというと動的リフレクションのことを指すが、静的リフレクションをサポートするプログラミング言語もある。リフレクションはSmalltalk、Java、.NET Frameworkのような仮想機械やインタプリタ上で実行されることを想定した言語でサポートされることが多く、C言語のような機械語として出力されることを想定した言語でサポートされることは少ない。

一般に、リフレクションとはオブジェクトがそれ自身の構造や計算上の意味を取得することを可能にするものである。リフレクションによるプログラミングパラダイムを//リフレクティブプログラミング//  という。

通常、プログラムのソースコードがコンパイルされると、プログラムの構造などの情報は低レベルコードに変換される過程で失われてしまう。リフレクションをサポートする場合、そのような情報は生成されるコードの中にメタデータとして保存される。

LISPやForthなど実行時とコンパイル時の区別のない言語では、コードの解釈とリフレクションとの間に違いはない。

2. 例
----

2.1. Java
---------

次のコードはjava.lang.reflectパッケージを使ったJava 6以降での例である。 // リフレクションなし Foo foo = new Foo; foo.hello;

// リフレクション Class cl = Class.forName; Method method = cl.getMethod; method.invoke);

どちらのコードでもFooクラスのインスタンスを作成し、そのインスタンスのhelloメソッドを呼んでいる。 前者の例では、クラス名やメソッド名がハードコーディングされているので実行時に他のクラスに変更するのは不可能である。リフレクションを用いた後者の例では、それらを実行時に容易に変更することができる。しかしその一方で、後者は読みにくく、またコンパイル時チェックの恩恵も得られない。つまり、もしFooクラスが存在しなかったとしたら前者のコードではコンパイル時にエラーとなるが、後者のコードでは実行するまでエラーが発生しない。

2.2. Perl
---------

次のコードは同じ例をPerlで書いたものである。

リフレクションなしFoo->new->hello;<WtXmlEndTag />

リフレクションmy $class = "Foo"; my $method = $class->can; $class->new->$method;<WtXmlEndTag />

2.3. Objective-C
----------------

次のコードは同じ例をObjective-Cで書いたものである。 // リフレクションなし [[[Foo alloc] init] hello];

// リフレクション id aClass = objc_getClass; SEL aSelector = NSSelectorFromString; objc_msgSend;

2.4. ActionScript
-----------------

次の例は同じ例をActionScriptで書いたものである。 // リフレクションなし var foo:Foo = new Foo; foo.hello;

// リフレクション var ClassReference:Class = flash.utils.getDefinitionByName as Class; var instance:Object = new ClassReference; instance.hello;

2.5. JavaScript
---------------

次の例は同じ例をJavaScriptで書いたものである。 // リフレクションなし var foo = new Foo; foo.hello;

// リフレクション var foo = this['Foo']; var methodName = 'hello'; )[methodName];

// Reflectオブジェクトを使用 const foo = Reflect.construct const hello = Reflect.get Reflect.apply

2.6. Ruby
---------

次の例は同じ例をRubyで書いたものである。

リフレクションなしfoo = Foo.new foo.hello

リフレクションfoo_class = Object.const_get 'Foo' foo = foo_class.new foo.send 'hello'<WtXmlEndTag />

2.7. Python
-----------

次の例は同じ例をPythonで書いたものである。

リフレクションなしobj = Foo obj.hello

リフレクションclass_name = "Foo" method = "hello" obj = globals[class_name] getattr

evaleval.hello")

<WtXmlEndTag />

2.8. PHP
--------

次の例は同じ例をPHPで書いたものである。 // リフレクションなし $foo = new Foo; $foo->hello;

// リフレクション $reflector = new ReflectionClass; $foo = $reflector->newInstance; $hello = $reflector->getMethod; $hello->invoke;

// コールバックの使用 $foo = new Foo; call_user_func);

// 可変変数構文の使用 $className = 'Foo'; $foo = new $className; $method = 'hello'; $foo->$method;<WtXmlEndTag />

2.9. C#
-------

次の例はC#による例で、より進んだリフレクションの用法を示している。プログラムはコマンドラインからアセンブリ名を入力としてとる。アセンブリとはクラスライブラリのようなものである。アセンブリが読み込まれると、アセンブリ内で定義されたメソッドを検索するためにリフレクションが用いられる。見つかった各メソッドに対し、最近変更があったかどうかをリフレクションを使って調べている。もし変更があり、かつ引数をとらないメソッドであれば、メソッド名と戻り値の型を表示する。

最近変更されたかどうかを調べるために、開発者はカスタム属性を使う必要がある。

using System; using System.Collections.Generic; using System.Text; using System.Reflection; using Recent;

namespace Reflect { class Program { private Assembly a; Program { a = Assembly.Load); // 指定されたアセンブリにSupportsRecentlyModified属性が適用されていることを確認。 Attribute c = Attribute.GetCustomAttribute); if  { // "SupportsRecentlyModified"属性の取得に失敗。 // つまり、アセンブリは"RecentlyModified"属性をサポートしていない。 throw new Exception; } Console.WriteLine; }

public void FindNewMethodsWithNoArgs { // アセンブリに定義されている型をすべて取得する Type[] t = a.GetTypes; foreach  { // この型がクラスでなければスキップ。 if  continue; Console.WriteLine; MethodInfo[] methods = type.GetMethods; foreach  { object[] ab = method.GetCustomAttributes, true); // 属性がひとつも取得できなければ、このメソッドは古いということである。 if  { // そうでなければただ1つの属性のみ取得される。 // なぜなら、"RecentlyModified"属性は他の属性との併用ができないからである。

Console.Write; if .Length > 0) break;

// 開発者が指定したコメントを取得するために、 // "RecentlyModifiedAttribute"属性のインスタンスを用いる。 Console.WriteLine.comment); Console.WriteLine; } } } }

static void Main { try { Program reflector = new Program; reflector.FindNewMethodsWithNoArgs; } catch  { Console.Error.WriteLine; } } } }<WtXmlEndTag />

上で使用したカスタム属性の実装を次に示す。

using System; using System.Collections.Generic; using System.Text;

namespace Recent { // この属性はメソッドにしか適用できず、また他の属性との併用もできない。 [AttributeUsage] public class RecentlyModifiedAttribute : Attribute { // この属性はメソッドに対して適用される。 // 引数なしで、 // またはコメントとともに])適用できる。

private String Comment = "このメソッドは最近変更されました。";

public RecentlyModifiedAttribute { // 属性のインスタンス化のための空コンストラクタ。 // 必須な引数はないのでコンストラクタは空。 }

// 省略可能な引数"comment"の定義。属性が使用される際に名前つき引数として指定される。 public String comment { get { return Comment; } set { Comment = comment; } } }

[AttributeUsage] public class SupportsRecentlyModifiedAttribute : Attribute { // この属性は引数なしでアセンブリに適用される。 // as in [SupportsRecentlyModified] public SupportsRecentlyModifiedAttribute { // 必須な引数はないのでコンストラクタは空。 } } }<WtXmlEndTag />

また、このカスタム属性を使用したクラスの定義例を次に示す。

using System; using System.Collections.Generic; using System.Text; using Recent;

// アセンブリが "RecentlyModified"属性をサポートすることを示すために // "SupportsRecentlyModified"属性を適用する。 [assembly: SupportsRecentlyModified]

namespace Reflect { class UseAttributes { private Object info;

public UseAttributes { info =  "Hello World"; }

public void OldMethodWithNoArgs { Console.WriteLine; }

// メソッドが最近変更されたということを示すために"RecentlyModified"属性を適用する。 [RecentlyModified] public void NewMethodWithNoArgs { Console.WriteLine; }

public void OldMethodWithOneArg { info = something; Console.WriteLine; }

[RecentlyModified] public void NewMethodWithOneArg { info = something; Console.WriteLine; } } }<WtXmlEndTag />

2.10. Delphi
------------

次の例は同じ例をDelphiで書いたものである。クラス 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 as TRttiInstanceType; Foo := RttiType.GetMethod.Invoke.AsObject; try RttiType.GetMethod.Invoke; finally Foo.Free; end; end;<WtXmlEndTag />

Delphiはアンマネージドでネイティブコンパイルされる言語であるため、注目に値する例となっている。リフレクションをサポートする言語の多くはPerlやPython、PHPのような動的プログラミング言語またはスクリプト言語であるか、あるいはJavaやC#のようにランタイムを必要とする言語である。

3. 関連項目
-------

自己書き換えコード
自己反映計算