C#
reflection

c#のリフレクション

More than 1 year has passed since last update.


リフレクションについて

リフレクションとは、メタデータを用いてデータにアクセスすることです。

リフレクションの使い方が分かると、動的言語話者が静的言語と仲良くなれると言われています[誰によって?]

どんなときに利用したくなるか、どうやって利用するかを説明します。


利用したくなるケース

class Character

{
public int HP;
public int Attack;
public int Speed;
}
class Enemy
{
public int HP;
public int Attack;
public int Speed;
}

上記のような2クラスがあり、CharacterからEnemyに攻撃する際のダメージを計算する関数を書くとします。

int CalcDamage(Character character, Enemy enemy)

{
return (character.Attack - enemy.Speed / 4) * Random.Range(100, 156) / 256;
}

さて、EnemyからCharacterに攻撃する際のダメージを計算する関数も作りましょう。

int CalcDamage(Enemy enemy, Character character)

{
return (enemy.Attack - character.Speed / 4) * Random.Range(100, 156) / 256;
}

全く同じような関数ができてしまいました。


リフレクションの使い方

今回の問題の解決方法としては、CharacterEnemyに共通のinterfaceを定義するか、親クラスを定義して継承するやり方が相応しいと思われます。

今回はリフレクションを解説する記事なので、強引にリフレクションを利用して解決してみます。

まず、c#にはTypeというクラスがあります

リフレクションはこのTypeオブジェクトを取得するところから始まります。

int CalcDamage(object attacker, object receiver)

{
Type attackerType = attacker.GetType();
...
}

c#ではobjectGetTypeメソッドが定義されていて、全てのオブジェクトのTypeをこれで取得することができます。

上記の例の場合、attackerCharacterオブジェクトを渡した場合、Characterクラスの情報(メタデータ)を持ったTypeオブジェクトがattackerTypeに代入されます。

Typeオブジェクトからクラスのメタデータを取得しましょう。

using System.Reflection;

...
Type attackerType = attacker.GetType();
FieldInfo hpField = attackerType.GetField("Attack");

FieldInfoクラスはSystem.Reflectionnamespaceのクラスなので、usingを忘れずに追加します。

TypeクラスにはGetFieldメソッドが定義されており、これを使って変数の名前からFieldInfoオブジェクトを取得できます。

FieldInfoオブジェクトを利用すると、実際にオブジェクトに設定された値を取得したり、オブジェクトに値を設定したりできます。

それでは、リフレクションを利用した場合の実装例です。

int CalcDamage(object attacker, object receiver)

{
FieldInfo attackField = attacker.GetType().GetField("Attack");
int attack = (int)attackField.GetValue(attacker);
FieldInfo speedField = receiver.GetType().GetField("Speed");
int speed = (int)speedField.GetValue(receiver);
return (attack - speed / 4) * Random.Range(100, 156) / 256;
}

注意したいのは、上記の例のattackFieldはあくまでattackerTypeAttack変数のメタデータであり、receiverAttackの取得には利用できないことです。

CharacterAttackと、EnemyAttackはそれぞれ別のFieldInfoということです。


まとめ



  • GetTypeGetFieldを利用して、変数名が同じなら型が違っても変数を取得できる。


  • GetPropertyを利用して、プロパティでも同じことができる。


  • GetMethodを利用して、メソッドの呼び出しを行うことができる。

  • c#4.0以降が利用できるのであれば、dynamicで同じようなことができる。

  • 今回のようなパターンでは、リフレクションを使わずに設計を見直すほうが有効である。