55
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

c#のリフレクション

Last updated at Posted at 2017-06-06

##リフレクションについて

リフレクションとは、メタデータを用いてデータにアクセスすることです。
リフレクションの使い方が分かると、動的言語話者が静的言語と仲良くなれると言われています~~[誰によって?]~~。
どんなときに利用したくなるか、どうやって利用するかを説明します。

##利用したくなるケース

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で同じようなことができる。
  • 今回のようなパターンでは、リフレクションを使わずに設計を見直すほうが有効である。
55
50
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?