xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

参照設定せずにFormを表示する

ハンズオン勉強会、Hokuriku.NET C# メタプログラミング ~リフレクション~に参加しました。その中で、アセンブリの動的読み込みをする時間がありました。参照設定に加えられていないアセンブリに含まれているクラスのインスタンスを作る、というものです。その時間ちょっとハデ目にFormを出して遊んでみたので、その時のコードを紹介します。

やってみたこと

全体としては次のようなことやってみました。

  1. コンソールアプリのプロジェクトで開始する
  2. 参照設定なしでFormを表示する
  3. Formの上にボタンを配置する
  4. ボタンがクリックされたら、コンソール画面上に文字を表示する

こんな他愛もないことをコンソールアプリのプロジェクトでやる辺りに自己満足感があるということで...。

サンプルコード & 実行例

以下がそのサンプルコードです。動的に作成したインスタンスはobject型として返される上、(参照設定を入れていないので)型変換もできないので、PropertyInfo / MethodInfo / EventInfoをガリガリ使って値を設定します。今回はリフレクションなお題ということで使っていませんが、dynamicを使えばもう少し楽に書くことができます。

using System;
using System.Reflection;

namespace ShowFormDynamically
{
    class Program
    {
        static void Main()
        {
            //--- Formsアセンブリ読み込み
            var assembly = Assembly.LoadFile(@"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.5\\System.Windows.Forms.dll");

            //--- Buttonを生成
            var button = assembly.CreateInstance("System.Windows.Forms.Button");

            //--- Buttonに表示する文字を設定
            button.GetType().GetProperty("Text").SetValue(button, "Click here!!");

            //--- Buttonのクリックイベントを設定
            EventHandler handler = (sender, e) => Console.WriteLine("クリックしたー");
            button.GetType().GetEvent("Click").AddEventHandler(button, handler);

            //--- Buttonを親コントロールにFill Docking
            var dockStyleType = assembly.GetType("System.Windows.Forms.DockStyle");
            var fill          = Enum.Parse(dockStyleType, "Fill");
            button.GetType().GetProperty("Dock").SetValue(button, fill);

            //--- Formインスタンスを生成
            var form = assembly.CreateInstance("System.Windows.Forms.Form");

            //--- Formのキャプションを設定
            form.GetType().GetProperty("Text").SetValue(form, "Viva Reflection!!");

            //--- ButtonをFormに乗せる
            var controls = form.GetType().GetProperty("Controls").GetValue(form);
            controls.GetType().GetMethod("Add").Invoke(controls, new object[]{ button });

            //--- 起動!
            form.GetType().GetMethod("ShowDialog", Type.EmptyTypes).Invoke(form, null);
        }
    }
}

結果は次のようになります。ボタンをクリックすると、ちゃんとコンソール画面に文字列が表示されます。

ShowFormDynamically

まとめ

リフレクションはちょっとした奥義みたいなものです。上記のようなジョークはさて置き、サードパーティ製ライブラリのprivateやinternalなプロパティから値を無理やり取得したりなんてこともできます。実行速度がすこぶる悪いので多用は禁物ですが、利用する目的を明確にして採用したいところです。

たまにこうやってリフレクション脳を鍛えるようにすると、いざというときに役立つかもしれません。

その他の参加者レポート

Hokuriku.NET C# メタプログラミング ~リフレクション~ 感想
[C#][dynamic] リフレクション Q&A