コンピュータサイエンスにおいて、リフレクションプログラミングまたはリフレクションとは、プロセスが自身の構造と動作を検査、内省、修正する能力のことである。 [1]
歴史的背景
最も初期のコンピュータは、命令をデータとして定義し、自己修正コードを使用することでプログラミングできたため、本質的にリフレクションを備えたネイティブのアセンブリ言語でプログラミングされていました。プログラミングの大部分がAlgol、Cobol、Fortran、Pascal、Cなどの高水準コンパイル言語に移行すると、このリフレクション機能は、型システムにリフレクションが組み込まれた新しいプログラミング言語が登場するまで、ほとんど姿を消しました。[出典が必要]
ブライアン・キャントウェル・スミスの1982年の博士論文では、手続き型プログラミング言語における計算リフレクションの概念と、 3-Lispの構成要素としてのメタ循環インタプリタの概念が導入されました。 [2] [3]
用途
リフレクションは、プログラマーが汎用ソフトウェア ライブラリを作成して、データの表示、さまざまな形式のデータの処理、通信用のデータのシリアル化とデシリアル化の実行、コンテナーや通信バースト用のデータのバンドルとバンドル解除を行うのに役立ちます。
リフレクションを効果的に使用するには、ほとんどの場合、設計フレームワーク、エンコードの説明、オブジェクト ライブラリ、データベースまたはエンティティ関係のマップなどの計画が必要です。
リフレクションにより、言語はネットワーク指向のコードにより適したものになります。たとえば、リフレクションは、シリアライゼーション、バンドル、およびさまざまなデータ形式用のライブラリを有効にすることで、 Javaなどの言語がネットワークで適切に動作するように支援します。Cなどのリフレクションのない言語では、シリアル化およびバンドル用のコードを生成するために、 抽象構文記法などのタスク用の補助コンパイラを使用する必要があります。
リフレクションは、実行時にプログラムの実行を監視および変更するために使用できます。リフレクション指向のプログラム コンポーネントは、コードの囲みの実行を監視し、その囲みの目的に応じて自分自身を変更できます。これは通常、実行時にプログラム コードを動的に割り当てることによって実現されます。
Javaなどのオブジェクト指向プログラミング言語では、リフレクションを使用すると、コンパイル時にインターフェース、フィールド、メソッドの名前を知らなくても、実行時にクラス、インターフェース、フィールド、メソッドを検査できます。また、新しいオブジェクトのインスタンス化やメソッドの 呼び出しも可能になります。
リフレクションは、モックオブジェクトの実行時作成/インスタンス化など、ソフトウェア テストの一部としてよく使用されます。
リフレクションはメタプログラミングの重要な戦略でもあります。
C#やJavaなどの一部のオブジェクト指向プログラミング言語では、リフレクションを使用してメンバーのアクセシビリティルールをバイパスできます。C# プロパティの場合、非公開プロパティの (通常は非表示の) バッキング フィールドに直接書き込むことでこれを実現できます。クラスや型の非公開メソッドを見つけて手動で呼び出すこともできます。これは、プロジェクト内部のファイルだけでなく、.NETのアセンブリや Java のアーカイブなどの外部ライブラリでも機能します。
実装
リフレクションをサポートする言語は、低レベル言語では実現が難しい、実行時に利用できる多くの機能を提供します。これらの機能には、次のような機能があります。
- 実行時にソースコード構造 (コード ブロック、クラス、メソッド、プロトコルなど) をファーストクラス オブジェクトとして検出し、変更します。
- クラスまたは関数のシンボリック名に一致する文字列を、そのクラスまたは関数への参照または呼び出しに変換します。
- 実行時にソースコード ステートメントであるかのように文字列を評価します。
- プログラミング構造に新しい意味や目的を与えるために、言語のバイトコード用の新しいインタープリターを作成します。
これらの機能は、さまざまな方法で実装できます。MOO では、リフレクションは日常のプログラミング言語の自然な一部を形成します。動詞 (メソッド) が呼び出されると、呼び出しのコンテキストを提供するために、 verb (呼び出される動詞の名前) やthis (動詞が呼び出されるオブジェクト) などのさまざまな変数に値が設定されます。セキュリティは通常、呼び出し元スタックにプログラムでアクセスすることによって管理されます。callers () は、現在の動詞が最終的に呼び出されたメソッドのリストであるため、callers()[0] (元のユーザーが呼び出したコマンド) に対してテストを実行すると、動詞が不正使用から自身を保護できます。
コンパイル言語は、ソースコードに関する情報を提供するためにランタイムシステムに依存しています。たとえば、コンパイルされたObjective-C実行可能ファイルは、実行可能ファイルのブロック内のすべてのメソッドの名前を記録し、プログラムにコンパイルされた基礎となるメソッド (またはこれらのメソッドのセレクター) とこれらを対応付けるテーブルを提供します。Common Lispなどの関数のランタイム作成をサポートするコンパイル言語では、ランタイム環境にコンパイラまたはインタープリターが含まれている必要があります。
プログラム変換システムを使用して自動ソースコード変更を定義することにより、 組み込みリフレクションのない言語にリフレクションを実装できます。
セキュリティに関する考慮事項
リフレクションにより、ユーザーはアプリケーションを通じて予期しない制御フローパスを作成し、セキュリティ対策を回避できる可能性があります。これは攻撃者に悪用される可能性があります。 [4]安全でないリフレクションによって引き起こされた Java の過去の脆弱性により、信頼できない可能性のあるリモートマシンから取得したコードが Javaサンドボックスのセキュリティメカニズムを突破できました。2013 年に行われた 120 件の Java 脆弱性に関する大規模な調査では、安全でないリフレクションは Java で最も一般的な脆弱性であるものの、最も悪用されている脆弱性ではないという結論が出ました。[5]
例
次のコード スニペットは、クラスのインスタンス を作成し、そのメソッドを呼び出します。プログラミング言語ごとに、通常の呼び出しシーケンスとリフレクション ベースの呼び出しシーケンスが示されています。
foo Foo PrintHello
コモンリスプ
以下は、 Common Lisp オブジェクト システムを使用したCommon Lispの例です。
( defclass foo () ()) ( defmethod print-hello (( f foo )) ( format T "Hello from ~S~%" f ))
;; 通常、リフレクションなし
( let (( foo ( make-instance 'foo ))) ( print-hello foo ))
;; リフレクションを使用して、「foo」という名前のクラスと、
「foo」に特化した「print-hello」という名前のメソッドを検索します。
( let* (( foo-class ( find-class ( read-from-string "foo" ))) ( print-hello-method ( find-method ( symbol-function ( read-from-string "print-hello" )) nil ( list foo-class )))) ( funcall ( sb-mop:method-generic-function print-hello-method ) ( make-instance foo-class )))
C#
以下はC#の例です。
// リフレクションなし
var foo = new Foo (); foo . PrintHello ();
//リフレクションありObject foo = Activator.CreateInstance ( "complete.classpath.and.Foo" ) ; MethodInfo method = foo.GetType ( ) . GetMethod ( " PrintHello" ); method.Invoke ( foo , null ) ;
Delphi、Object Pascal
このDelphiおよびObject Pascal の例では、TFooクラスがUnit1というユニットで宣言されていることを前提としています。
RTTI 、Unit1 を使用します。
手順WithoutReflection ; var Foo : TFoo ; begin Foo : = TFoo.Create ; try Foo.Hello ; finally Foo.Free ; end ; end ;
手順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 の例です。
// リフレクションなし
Foo foo { }; foo . hello ();
// リフレクションあり
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の例です。
「reflect」をインポートする
// リフレクションなし
f := Foo {} f . Hello ()
// リフレクションあり
fT := reflect . TypeOf ( Foo {}) fV := reflect . New ( fT )
m : = fV.MethodByName ( " Hello " ) if m.IsValid ( ) { m.Call ( nil ) }
ジャワ
以下はJavaの例です。
java.lang.reflect.Methodをインポートします。
// リフレクションなし
Foo foo = new Foo (); foo . hello ();
// リフレクションを使用
try { Object foo = Foo . class . getDeclaredConstructor (). newInstance ();
メソッドm = foo . getClass (). getDeclaredMethod ( "hello" , new Class <?>[ 0 ] ); m . invoke ( foo ); } catch ( ReflectiveOperationExceptionは無視されます) {}
JavaScript
以下はJavaScriptの例です。
// リフレクションなし
const foo = new Foo () foo . hello ()
//リフレクションありconst foo = Reflect.construct ( Foo ) const hello = Reflect.get ( foo , ' hello ' ) Reflect.apply ( hello , foo , [ ] )
// eval の場合
eval ( 'new Foo().hello()' )
ジュリア
以下はJuliaの例です。
julia>構造体Point x :: Int y終了
# リフレクションによる検査
julia> fieldnames ( Point ) (:x, :y)
julia>フィールドタイプ( Point ) (Int64, Any)
ジュリア> p =点( 3 , 4 )
# リフレクションによるアクセス
julia> getfield ( p , :x ) 3
オブジェクティブC
以下は、 OpenStepまたはFoundation Kitフレームワークの いずれかが使用されていることを示すObjective-Cの例です。
// Foo クラス。
@interface Foo : NSObject
- ( void ) hello ;
@end
// リフレクションなしで Foo インスタンスに "hello" を送信します。
Foo * obj = [[ Foo alloc ] init ]; [ obj hello ];
// リフレクションを使用して Foo インスタンスに "hello" を送信します。
id obj = [[ NSClassFromString ( @"Foo" ) alloc ] init ]; [ obj performSelector : @selector ( hello )];
パール
以下はPerlの例です。
# リフレクションなし
my $foo = Foo -> new ; $foo -> hello ;
# または
Foo -> new -> hello ;
# リフレクションを使用
my $class = "Foo" my $constructor = "new" ; my $method = "hello" ;
私の$f = $class -> $constructor ; $f -> $method ;
# または
$class -> $constructor -> $method ;
# eval で
eval "new Foo->hello;" ;
PHP の
以下はPHPの例です: [6]
// リフレクションなし
$foo = new Foo ();
$foo -> hello ();
// リフレクション付き、Reflections API を使用
$reflector = new ReflectionClass ( "Foo" );
$foo = $reflector -> newInstance ();
$hello = $reflector -> getMethod ( "hello" );
$hello -> invoke ( $foo );
パイソン
以下はPythonの例です。
# リフレクションなし
obj = Foo ()
obj . hello ()
# リフレクション付き
obj = globals ()[ "Foo" ]()
getattr ( obj , "hello" )()
# eval の場合
eval ( "Foo().hello()" )
R
以下はRの例です。
# リフレクションなしで、foo() がメソッド "hello" を持つ S3 型オブジェクトを返すと仮定します。obj
< - foo () hello ( obj )
# リフレクションあり
class_name <- "foo" generic_having_foo_method <- "hello" obj <- do.call ( class_name , list ()) do.call ( generic_having_foo_method , alist ( obj ))
ルビー
以下はRubyの例です。
# リフレクションなし
obj = Foo . new obj . hello
# リフレクション付き
obj = Object . const_get ( "Foo" ) . new obj . send :hello
# eval で
eval "Foo.new.hello"
ゾジョ
以下はXojoを使用した例です。
' リフレクションなし
Dim fooInstance As New Foo fooInstance . PrintHello
'リフレクションありDim classInfo As Introspection.Typeinfo = GetTypeInfo ( Foo ) Dimconstructors ( ) 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
参照
参考文献
引用
- ^ 行動リフレクションとその実装に関するチュートリアル、Jacques Malenfant 他(PDF) 、不明、 2017 年 8 月 21 日のオリジナル(PDF)からアーカイブ、2019 年6 月 23 日取得
- ^ Brian Cantwell Smith、「プログラミング言語における手続き型リフレクション」、マサチューセッツ工科大学電気工学・コンピュータサイエンス学部、博士論文、1982年。
- ^ Brian C. Smith. 手続き型言語におけるリフレクションとセマンティクス Archived 2015-12-13 at the Wayback Machine . 技術レポート MIT-LCS-TR-272、マサチューセッツ工科大学、マサチューセッツ州ケンブリッジ、1982 年 1 月。
- ^ Barros, Paulo; Just, René; Millstein, Suzanne; Vines, Paul; Dietl, Werner; d'Amorim, Marcelo; Ernst, Michael D. (2015 年 8 月)。暗黙的な制御フローの静的分析: Java リフレクションと Android インテントの解決(PDF) (レポート)。ワシントン大学。UW-CSE-15-08-01。2021年10 月 7 日閲覧。
- ^ Eauvidoum, Ieu; disk noise (2021年10月5日). 「Javaサンドボックスからの脱出20年」. Phrack . 第10巻第46号. 2021年10月7日閲覧。
- ^ 「PHP: ReflectionClass - マニュアル」www.php.net。
出典
- Jonathan M. Sobel と Daniel P. Friedman。リフレクション指向プログラミング入門 (1996)、インディアナ大学。
- コード窃盗を防ぐための C# および C++/CLI ラッパーを使用した反射防止技術
さらに読む
- Ira R. Forman および Nate Forman、「Java Reflection in Action」 (2005)、ISBN 1-932394-18-4
- アイラ・R・フォーマンとスコット・ダンフォース著『メタクラスの活用』(1999年)、ISBN 0-201-43305-2
外部リンク
- 論理プログラミング、関数型プログラミング、オブジェクト指向プログラミングにおけるリフレクション: 短い比較研究
- リフレクション指向プログラミング入門
- Brian Foote の Smalltalk でのリフレクションに関するページ
- Oracle の Java Reflection API チュートリアル
参照
- {{コンピュータ言語}}
- {{プログラミング言語}}
- {{プログラミングパラダイム navbox }}
