memo.txt

教えていただいたこと、勉強したことのメモです。

リフレクションを教えてもらった

リフレクションとは

  1. 動的(実行時)に、型の情報/メソッドの情報などを取得する。
  2. 取得した情報を元に、インスタンスの生成/メソッドの呼び出し行う。

動的に型の生成・書き換え/メソッドの生成・書き換え(もするらしい)。

どういう時に使うのでしょう

文字列からオブジェクトを生成し、メソッドを呼び出す。

設定ファイルにクラス名(例:Hoge)が文字列で書いてある。
プログラムが設定ファイルのクラス名を読み込み、リフレクションを使ってHoge型のオブジェクトを生成する。

f:id:rika0618:20131011124304j:plain

ToString

フィールドの中身を文字列で表示するToString()を用意するとこんな感じになる。
→ int y みたいなフィールドが増えると、ToString()を書き換えないといけない。

class Hoge
{
    int x;
    string str;

    public override string ToString() {
    return "Hoge(x = " + x + ", str = " + str + ")";
    }
}

そこで、リフレクションを使ってこんな感じに書いておく。
→フィールドが増えてもToString()を書き換えなくていい。

public class Class2
{
    int x;
    int y;
    string str = "Test";

    public override string ToString()
    {
        var t = typeof(Class2);
        return "Hoge(" + string
            .Join(", ", t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .Select(f => f.Name + "=" + f.GetValue(this))) + ")";
    }

    static void Main() {
        var c = new Class2();
        Console.WriteLine(c.ToString());
    }
}

遅いけどな

実行時に処理するので遅いので、使い方注意。
特にf.GetValue(this)が10~100倍くらい遅い。

時間計測した。
    public class Hoge
    {
        int x;
        int y;
        string str = "Test";

        public override string ToString()
        {
            var t = typeof(Hoge);
            return "Class2(" + string
                .Join(", ", t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(f => f.Name + "=" + f.GetValue(this))) + ")";
        }

        static void Ignore(string str) { }

        static void Main() {
            var c = new Hoge();
            Console.WriteLine(c.ToString());

            const int Times = 10000000;
            var t = typeof(Hoge);
            var f = t.GetField("str", BindingFlags.NonPublic | BindingFlags.Instance);
            var sw = Stopwatch.StartNew();
            for (int i = 0; i < Times; i++)
            {
                Ignore((string)f.GetValue(c));
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

            sw.Reset();
            sw.Start();
            for (int i = 0; i < Times; i++)
            {
                Ignore(c.str);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }