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

概要

編集

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

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

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

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

編集

Java

編集

次のコードはjava.lang.reflectパッケージを使ったJava 6以降での例である。

// リフレクションなし
Foo foo = new Foo();
foo.hello();
// リフレクション
Class cl = Class.forName("Foo");
Method method = cl.getMethod("hello");
method.invoke(cl.newInstance());

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

Perl

編集

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

# リフレクションなし
Foo->new->hello();
# リフレクション
my $class = "Foo";
my $method = $class->can("hello");
$class->new->$method();

Objective-C

編集

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

// リフレクションなし
[[[Foo alloc] init] hello];
// リフレクション
id aClass = objc_getClass("Foo");
SEL aSelector = NSSelectorFromString(@"hello");
objc_msgSend([[aClass alloc] init], aSelector, nil);

ActionScript

編集

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

// リフレクションなし
var foo:Foo = new Foo();
foo.hello();
// リフレクション
var ClassReference:Class = flash.utils.getDefinitionByName("Foo") as Class;
var instance:Object = new ClassReference();
instance.hello();

JavaScript

編集

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

// リフレクションなし
var foo = new Foo();
foo.hello();
// リフレクション
var foo = this['Foo'];
var methodName = 'hello';
(new foo())[methodName]();
// Reflectオブジェクトを使用
const foo = Reflect.construct(Foo)
const hello = Reflect.get(foo, 'hello')
Reflect.apply(hello, foo, [])

Ruby

編集

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

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

# リフレクション
foo_class = Object.const_get 'Foo'
foo = foo_class.new
foo.send 'hello'

Python

編集

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

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

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

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

PHP

編集

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

// リフレクションなし
$foo = new Foo();
$foo->hello();

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

// コールバックの使用
$foo = new Foo();
call_user_func(array($foo, 'hello'));

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

C#

編集

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

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

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

namespace Reflect
{
    class Program
    {
        private Assembly a;
        Program(String assemblyName)
        {
            a = Assembly.Load(new AssemblyName(assemblyName));
            // 指定されたアセンブリにSupportsRecentlyModified属性が適用されていることを確認。
            Attribute c = Attribute.GetCustomAttribute(a, typeof(SupportsRecentlyModifiedAttribute));
            if (c == null)
            {
                 // "SupportsRecentlyModified"属性の取得に失敗。
                 // つまり、アセンブリは"RecentlyModified"属性をサポートしていない。
                 throw new Exception("アセンブリ" + assemblyName + " は必要な属性をサポートしていません。");
            }
            Console.WriteLine("読み込み完了: " + a.FullName);
        }

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

                        Console.Write("\t更新されたメソッド: " + method.Name);
                        if (method.GetParameters().Length > 0)
                            break;

                        // 開発者が指定したコメントを取得するために、
                        // "RecentlyModifiedAttribute"属性のインスタンスを用いる。
                        Console.WriteLine("\t" + (ab[0] as RecentlyModifiedAttribute).comment);
                        Console.WriteLine("\t\t戻り値: " + method.ReturnType.Name);
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            try
            {
                Program reflector = new Program("UseAttributes");
                reflector.FindNewMethodsWithNoArgs();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message);
            }
        }
    }
}

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

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

namespace Recent
{
    // この属性はメソッドにしか適用できず、また他の属性との併用もできない。
    [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
    public class RecentlyModifiedAttribute : Attribute
    {
        // この属性はメソッドに対して適用される。
        // 引数なしで([RecentlyModified])、
        // またはコメントとともに([RecentlyModified(comment="<someComment>")])適用できる。

        private String Comment = "このメソッドは最近変更されました。";
        
        public RecentlyModifiedAttribute()
        {
            // 属性のインスタンス化のための空コンストラクタ。
            // 必須な引数はないのでコンストラクタは空。
        }

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

    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)]
    public class SupportsRecentlyModifiedAttribute : Attribute
    {
        // この属性は引数なしでアセンブリに適用される。
        // as in [SupportsRecentlyModified]
        public SupportsRecentlyModifiedAttribute()
        {
            // 必須な引数はないのでコンストラクタは空。
        }
    }
}

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

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 = (object) "Hello World";
        }

        public void OldMethodWithNoArgs()
        {
            Console.WriteLine("これは引数をとらない古いメソッドである。");
        }

        // メソッドが最近変更されたということを示すために"RecentlyModified"属性を適用する。
        [RecentlyModified]
        public void NewMethodWithNoArgs()
        {
            Console.WriteLine("これは引数をとらない新しいメソッドである。");
        }

        public void OldMethodWithOneArg(object something)
        {
            info = something;
            Console.WriteLine("これは引数を1つとる古いメソッドである。");
        }

        [RecentlyModified]
        public void NewMethodWithOneArg(object something)
        {
            info = something;
            Console.WriteLine("これは引数を1つとる新しいメソッドである。");
        } 
    }
}

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('Unit1.TFoo') as TRttiInstanceType;
  Foo := RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsObject;
  try
    RttiType.GetMethod('Hello').Invoke(Foo, []);
  finally
    Foo.Free;
  end;
end;

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

関連項目

編集

📚 Artikel Terkait di Wikipedia

リンカーン・センター映画協会

of Lincoln Center)は、アメリカ合衆国の映画組織。略称はFSLC。1969年に設立。 ニューヨーク映画祭を主催。雑誌『Film Comment』を発行している。1972年よりChaplin Award Galaを開催しており、これまでにアルフレッド・ヒッチコック、フェデリコ・フェリー

C++17

“N4659: Working Draft, Standard for Programming Language C++”. 2017年3月24日閲覧。 ^ “N4659: Working Draft, Standard for Programming Language C++”. 2017年3月24日閲覧。 ^

OpenMP

5.2”. OpenMP (2021年11月9日). 2025年5月16日閲覧。 ^ “OpenMP ARB Releases Public Comment Draft of OpenMP 6.0”. OpenMP (2024年8月1日). 2025年5月16日閲覧。 オープン標準 OpenCL OpenACC

ALGOL

COMMENT The absolute greatest element of the matrix a, of size ⌈a by 2⌈a is transferred to y, and the subscripts of this element to i and k; COMMENT BEGIN

OpenACC

Accelerator Programming Standard”. HPCwire. 20 June 2012. 2012年6月23日時点のオリジナルよりアーカイブ. 2014年1月14日閲覧. ^ “OpenACC Version 2.0 Posted for Comment”. OpenACC.org

Kaleidoscope (高見沢俊彦のアルバム)

Takamizawa Kaleidoscope Network Luke Takamura Comment Anchang Comment KOJI Comment Kohey Tsuchiya Comment 千年ロマンス Video Clip 各参加者横の数字は参加した楽曲のトラック番号を表す。

GOOD LUCK MY WAY

2022年1月23日時点のオリジナルよりアーカイブ. 2022年1月23日閲覧. ^ a b c d e “ARTIST SPECIAL COMMENT”. 鋼の錬金術師 嘆きの丘の聖なる星. 2022年12月9日時点のオリジナルよりアーカイブ. 2022年12月9日閲覧. ^ “俺たちを待ってたんだろ?

計算機プログラムの構造と解釈

2020年6月21日閲覧。 ^ Guy, Donald, “The End of an Era”, MIT Admissions (blog comment), オリジナルの2018-08-21時点におけるアーカイブ。, https://web.archive