##リフレクションについて
リフレクションとは、メタデータを用いてデータにアクセスすることです。
リフレクションの使い方が分かると、動的言語話者が静的言語と仲良くなれると言われています~~[誰によって?]~~。
どんなときに利用したくなるか、どうやって利用するかを説明します。
##利用したくなるケース
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;
}
全く同じような関数ができてしまいました。
##リフレクションの使い方
今回の問題の解決方法としては、Character
とEnemy
に共通のinterfaceを定義するか、親クラスを定義して継承するやり方が相応しいと思われます。
今回はリフレクションを解説する記事なので、強引にリフレクションを利用して解決してみます。
まず、c#にはType
というクラスがあります。
リフレクションはこのType
オブジェクトを取得するところから始まります。
int CalcDamage(object attacker, object receiver)
{
Type attackerType = attacker.GetType();
...
}
c#ではobject
にGetType
メソッドが定義されていて、全てのオブジェクトのType
をこれで取得することができます。
上記の例の場合、attacker
にCharacter
オブジェクトを渡した場合、Character
クラスの情報(メタデータ)を持ったType
オブジェクトがattackerType
に代入されます。
Type
オブジェクトからクラスのメタデータを取得しましょう。
using System.Reflection;
...
Type attackerType = attacker.GetType();
FieldInfo hpField = attackerType.GetField("Attack");
FieldInfo
クラスはSystem.Reflection
namespaceのクラスなので、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
はあくまでattacker
のType
のAttack
変数のメタデータであり、receiver
のAttack
の取得には利用できないことです。
Character
のAttack
と、Enemy
のAttack
はそれぞれ別のFieldInfo
ということです。
##まとめ
-
GetType
、GetField
を利用して、変数名が同じなら型が違っても変数を取得できる。 -
GetProperty
を利用して、プロパティでも同じことができる。 -
GetMethod
を利用して、メソッドの呼び出しを行うことができる。 - c#4.0以降が利用できるのであれば、
dynamic
で同じようなことができる。 - 今回のようなパターンでは、リフレクションを使わずに設計を見直すほうが有効である。