リフレクションを教えてもらった
リフレクションとは
- 動的(実行時)に、型の情報/メソッドの情報などを取得する。
- 取得した情報を元に、インスタンスの生成/メソッドの呼び出し行う。
動的に型の生成・書き換え/メソッドの生成・書き換え(もするらしい)。
どういう時に使うのでしょう
文字列からオブジェクトを生成し、メソッドを呼び出す。
設定ファイルにクラス名(例:Hoge)が文字列で書いてある。
プログラムが設定ファイルのクラス名を読み込み、リフレクションを使ってHoge型のオブジェクトを生成する。
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); } }