理由はわかりませんが、リフレクションを使用すると、常に「だまされている」ように感じます。おそらく、私が知っているパフォーマンスヒットが原因です。
私の一部は、それがあなたが使用している言語の一部であり、それがあなたがしようとしていることを成し遂げることができるなら、なぜそれを使用しないのかを言います。私の他の部分は、リフレクションを使用せずにこれを行う方法があるはずだと言います。たぶんそれは状況によると思います。
リフレクションを使用する際に注意する必要のある潜在的な問題は何ですか?それらについてどの程度心配する必要がありますか?従来の解決策を見つけるために費やす価値はどれくらいありますか?
いいえ、それは不正行為ではありません。これは、一部のプログラミング言語の問題を解決する方法です。
現在、それは多くの場合、最良の(最もクリーンで、最も単純で、最も保守しやすい)ソリューションではありません。より良い方法がある場合は、実際にそれを使用してください。ただし、そうでない場合もあります。または、存在する場合は、コードが重複するなど、非常に複雑になり、実行不可能になります(長期的に維持するのが困難です)。
現在のプロジェクト(Java)の2つの例:
fieldX
という名前のXML要素をクラスの適切なフィールドに一致させ、後者を初期化します。場合によっては、識別されたプロパティからその場で簡単なGUIダイアログボックスを構築できます。リフレクションがなければ、いくつかのアプリケーションで何百行ものコードが必要になります。したがって、リフレクションにより、手間をかけずに簡単なツールをすばやくまとめることができ、無関係なものではなく、重要な部分(Webアプリの回帰テスト、サーバーログの分析など)に集中できるようになりました。一番下の行は、他の強力なツールと同様に、反射も足で自分を撃つために使用できます。いつ、どのように使用するか(しないか)を学べば、そうでなければ難しい問題に対してエレガントでクリーンなソリューションを提供できます。悪用すると、単純な問題を複雑で醜い混乱に変えることができます。
それは不正行為ではありません。ただし、少なくとも次の理由により、通常は量産コードでは不適切です。
リフレクションの使用を次の場合に制限することをお勧めします。
他のすべてのケースでは、反射を回避するアプローチを考え出すことをお勧めします。 インターフェイスの定義と適切なメソッドを組み合わせて、メソッドを呼び出すクラスのセットに実装するだけで、ほとんどの単純なケースを解決できます。
リフレクションはメタプログラミングのもう1つの形式であり、最近ほとんどの言語で見られる型ベースのパラメーターと同じくらい有効です。リフレクションは強力で汎用的であり、リフレクションプログラムは(もちろん正しく使用した場合)保守性が高く、純粋にオブジェクト指向または手続き型のプログラムよりも優れています。はい、パフォーマンスの代償を払うことになりますが、多くの場合、またはほとんどの場合でも、より保守しやすい遅いプログラムを喜んで採用します。
確かにそれはすべてあなたが何を達成しようとしているのかに依存します。
たとえば、依存関係注入を使用してチェックするメディア(MP3ファイルまたはJPEGファイル)の種類を決定するメディアチェッカーアプリケーションを作成しました。シェルは、各タイプの関連情報を含むグリッドを表示する必要がありましたが、表示するwhatに関する知識がありませんでした。これは、そのタイプのメディアを読み取るアセンブリで定義されています。
したがって、グリッドを正しく設定できるように、表示する列の数とそのタイプおよび名前を取得するためにリフレクションを使用する必要がありました。また、他のコードや構成ファイルを変更せずに、注入されたライブラリを更新(または新しいライブラリを作成)できることも意味しました。
他の唯一の方法は、チェックするメディアの種類を切り替えたときに更新する必要がある構成ファイルを用意することでした。これにより、アプリケーションに別の障害点が発生します。
リフレクションは、開発者向けのツールを作成するのに最適です。
ビルド環境でコードを検査し、コードを操作/初期化するための適切なツールを生成できる可能性があるためです。
一般的なプログラミング手法としては便利ですが、ほとんどの人が想像するよりも脆弱です。
リフレクション(IMO)の実際の開発用途の1つは、汎用のストリーミングライブラリの記述を非常に簡単にすることです(クラスの記述が変更されない限り(非常に脆弱なソリューションになります))。
libraryの作成者である場合、Reflectionは素晴らしいツールであり、受信データに影響を与えません。リフレクションとメタプログラミングの組み合わせにより、ライブラリは任意の呼び出し元とシームレスに連携でき、コード生成などのループをジャンプする必要はありません。
私はapplicationコードでのリフレクションを阻止しようとしますが、アプリ層では、インターフェイス、抽象化、カプセル化など、さまざまなメタファを使用する必要があります。
リフレクションの使用は、OO言語で十分な認識をもって使用しないと、多くの場合有害です。
StackExchangeサイトで見た悪い質問の数を数えませんでした。
OOのポイントのほとんどはそれです
コードのいずれかの時点で、ポイント2が渡されたオブジェクトに対して有効でない場合、これらの1つ以上が真になります
スキルの低い開発者は、これを取得せず、コードのどの部分でも何でも渡すことができ、(ハードコードされた)可能性のセットから必要なことを実行できると信じています。これらの馬鹿は反射を使用しますたくさん。
OO言語の場合、リフレクションはメタアクティビティ(クラスローダー、依存性注入など)でのみ必要です。これらのコンテキストでは、操作/構成を支援する汎用サービスを提供しているため、リフレクションが必要です正当かつ正当な理由で何も知らないコードの例です。他のほとんどの状況では、リフレクションに到達している場合、何か間違っているので、自分に問いかける必要があります理由このコードは、渡されたオブジェクトについて十分に認識していません。
反映されたクラスのドメインが明確に定義されている場合の代替手段は、実行時にリフレクションを使用する代わりに、他のメタデータとともにリフレクションを使用するコードを生成するです。これはFreeMarker/FMPPを使用して行います。他にもたくさんのツールから選択できます。これの利点は、簡単にデバッグできる「実際の」コードなどになることです。
状況によっては、これによりコードを大幅に高速化することができます。リフレクションの欠点を回避します。
先に述べた。
リフレクションが不正行為のように感じられる場合、それはあなたが多くの当て推量に基づいているために確信が持てず、腸がこれが危険であることを警告しているためである可能性があります。リフレクションに固有のメタデータを独自のメタデータで強化する方法を必ず提供してください。ここで、遭遇する可能性のある実際のクラスのすべての癖および特殊なケースを説明できます。
Reflectionで発生した問題の1つは、ミックスにObfuscationを追加したときです。すべてのクラスに新しい名前が付けられ、その名前でクラスまたは関数を突然ロードすると機能しなくなります。
それは不正行為ではありませんが、他のツールと同様に、解決しようとしているものに使用する必要があります。リフレクションは、定義により、コードを介してコードを検査および変更できます。それがあなたがする必要があるものであるならば、反射は仕事のためのツールです。リフレクションはすべてメタコードに関するものです:(データを対象とする通常のコードとは対照的に)コードを対象とするコード。
リフレクションの適切な使用例は、一般的なWebサービスインターフェイスクラスです。典型的な設計は、ペイロードの機能からプロトコルの実装を分離することです。したがって、ペイロードを実装する1つのクラス(T
と呼びましょう)と、プロトコル(P
)を実装する別のクラスがあります。 T
は非常に単純です。実行するすべての呼び出しについて、想定されていることをすべて実行する1つのメソッドを記述するだけです。ただし、P
は、Webサービス呼び出しをメソッド呼び出しにマップする必要があります。このマッピングをジェネリックにすることは、冗長性を回避し、P
の再利用性を高めるために望ましいものです。リフレクションは、クラスT
のコンパイル時の知識なしに、実行時にクラスP
を検査し、Webサービスプロトコルを通じてT
に渡された文字列に基づいてそのメソッドを呼び出す手段を提供します。 「コードについてのコード」ルールを使用すると、クラスP
がデータの一部としてクラスT
にコードを持っていると主張できます。
しかしながら。
リフレクションは、言語の型システムの制限を回避するためのツールも提供します-理論的には、すべてのパラメーターをobject
型として渡し、リフレクションを通じてそれらのメソッドを呼び出すことができます。強力な静的型付け規則を適用することになっている言語であるVoilàは、遅延バインディングのある動的型付け言語のように動作しますが、構文がはるかに複雑です。私がこれまでに見たそのようなパターンのすべてのインスタンスはダーティなハックであり、常に、言語の型システム内のソリューションが可能であり、すべての点でより安全でエレガントで効率的でした。
いくつかの例外が存在します。たとえば、関連しないさまざまなタイプのデータソースにデータバインドできるGUIコントロールなどです。データをバインドできるようにデータに特定のインターフェースを実装することを義務付けるのは現実的ではなく、プログラマーに各タイプのデータソース用のアダプターを実装させることもありません。この場合、リフレクションを使用してデータソースのタイプを検出し、データバインディングを調整する方が便利です。
それは完全に依存します。リフレクションなしでは難しいことの例は、複製 ObjectListView です。また、オンザフライでILコードを生成します。
リフレクションは、慣習に基づいたシステムを作成する主要な方法です。それがほとんどのMVCフレームワーク内で頻繁に使用されていることを知って驚くことはありません。 ORMの主要なコンポーネントです。おそらく、あなたはすでにそれを使って構築されたコンポーネントを毎日使用しているでしょう。
このような使用法の代替策は構成ですが、これには独自の欠点があります。
リフレクションは、他の方法では実質的に実行できないことを実現できます。
たとえば、このコードを最適化する方法を検討してください。
int PoorHash(char Operator, int seed, IEnumerable<int> values) {
foreach (var v in values) {
seed += 1;
switch (char) {
case '+': seed += v; break;
case '^': seed ^= v; break;
case '-': seed -= v; break;
...
}
seed *= 3;
}
return seed;
}
内側のループの真ん中に高価なテストスマックがありますが、それを抽出するには、演算子ごとにループを1回書き換える必要があります。リフレクションを使用すると、ループを数十回繰り返すことなく(その結果、保守性を犠牲にすることなく)、そのテストの抽出と同等のパフォーマンスを得ることができます。必要なループをその場で生成してコンパイルするだけです。
私は 実際にこの最適化を行いました ですが、状況は少し複雑で、結果は驚くべきものでした。パフォーマンスが1桁向上し、コード行が少なくなりました。
(注:最初に、charではなくFuncを渡すのと同じことを試しましたが、わずかに優れていましたが、10倍近くの反射は達成されませんでした。)