読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-034 (System.Dynamic.ExpandoObject)(動的オブジェクト構築, dynamic, 4.0)

.NET Framework 4.0より、動的オブジェクトに対してのサポートが追加されています。
動的オブジェクトに関するクラスは、System.Dynamic名前空間に配置されています。
System.Dynamic名前空間は、「System.Core.dll」に存在します。


今回対象となるSystem.Dynamic.ExpandoObjectクラスも動的オブジェクトを扱う為に
追加されたクラスです。Expandoという名前が示す通り、javascriptのようにクラス定義が
行えます。


C#からこのクラスを扱う場合は、dynamicが必須となります。
どのように扱うのかは、実際にコードを見た方がイメージできると思います。


以下、サンプルです。

    public class ExpandoObjectSamples01 : IExecutable
    {
        public void Execute()
        {
            //////////////////////////////////////////////////////////////////////
            //
            // 動的オブジェクトを作成.
            //
            // System.Dynamic名前空間は、「System.Core.dll」内に存在する。
            // 動的オブジェクトを利用するには、上記のDLLの他に以下のDLLも参照設定
            // する必要がある。
            //
            // ・Microsoft.CSharp.dll
            //
            dynamic obj = new System.Dynamic.ExpandoObject();
            
            //
            // メンバーを定義.
            //
            // プロパティ.
            obj.Value = 10;
            
            // メソッド.
            var action = new Action<string>((line) =>
            {
                Console.WriteLine(line);
            });
            
            obj.WriteLine = action;
            
            //
            // 呼び出してみる.
            //
            obj.WriteLine(obj.Value.ToString());
            
            obj.Value = 100;
            obj.WriteLine(obj.Value.ToString());
            
            obj.Value = "hoge";
            obj.WriteLine(obj.Value.ToString());
        }
    }

上記サンプルを実行すると、

10
100
hoge

と表示されます。


今までだと、まず最初にクラスなり構造体なりを定義し、その後
インスタンス化し、値を適切に設定していました。


Python,Javascriptなどの動的言語の場合、実行時に必要となるクラス定義をその場で作成することは
珍しくありません。ExpandoObjectを利用することで、C#側でもそれが行えるようになります。
(当然VisualBasicでもC#と同じ事が行えます。)


上記のサンプルでは、WriteLineというメンバーに対して、Actionデリゲートを設定しています。
なので、当然イベントも設定することもできます。

    public class ExpandoObjectSamples02 : IExecutable
    {
        public void Execute()
        {
            ///////////////////////////////////////////////
            //
            // ExpandoObjectにイベントを追加.
            //
            dynamic obj = new System.Dynamic.ExpandoObject();
            
            //
            // イベント定義
            //     ExpandoObjectに対してイベントを定義するには
            //     まず、イベントフィールドを定義して、それをnullで初期化
            //     する必要がある。
            //
            obj.MyEvent = null;
            
            //
            // イベントハンドラを設定.
            //
            obj.MyEvent += new EventHandler((sender, e) =>
            {
                Console.WriteLine("sender={0}", sender);
            });
            
            // イベント着火.
            obj.MyEvent(obj, EventArgs.Empty);
        }
    }


動的言語では、クラスのメンバーの追加・削除を実行時に行う事がよくあります。
ExpandoObjectは、IDictionaryを実装しており、これを利用することで
動的にメンバーの追加・削除が行えます。

    public class ExpandoObjectSamples03 : IExecutable
    {
        public void Execute()
        {
            ///////////////////////////////////////////////////////////////////////
            //
            // ExpandoObjectをDictionaryとして扱う. (メンバーの追加/削除)
            //     ExpandoObjectはIDictionary<string, object>を実装しているので
            //     Dictionaryとしても利用出来る.
            //
            dynamic obj = new System.Dynamic.ExpandoObject();
            obj.Name = "gsf_zero1";
            obj.Age  = 30;
            
            //
            // 定義されているメンバーを列挙.
            //
            IDictionary<string, object> map = obj as IDictionary<string, object>;
            foreach (var pair in map)
            {
                Console.WriteLine("{0}={1}", pair.Key, pair.Value);
            }
            
            //
            // Ageメンバーを削除.
            //
            map.Remove("Age");
            
            //
            // 確認.
            //
            foreach (var pair in map)
            {
                Console.WriteLine("{0}={1}", pair.Key, pair.Value);
            }
            
            // エラーとなる.
            //Console.WriteLine(obj.Age);
        }
    }


また、ExpandoObjectはINotifyPropertyChangedインターフェースも実装しているので
これを利用することで、プロパティの変更タイミングを捉える事ができます。

    public class ExpandoObjectSamples04 : IExecutable
    {
        public void Execute()
        {
            ///////////////////////////////////////////////////////////////////////
            //
            // ExpandoObjectをINotifyPropertyChangedとして扱う. (プロパティの変更をハンドル)
            //
            dynamic obj = new System.Dynamic.ExpandoObject();
            
            //
            // イベントハンドラ設定.
            //
            (obj as INotifyPropertyChanged).PropertyChanged += (sender, e) =>
            {
                Console.WriteLine("Property Changed:{0}", e.PropertyName);
            };
            
            //
            // メンバー定義.
            //
            obj.Name = "gsf_zero1";
            obj.Age  = 30;
            
            //
            // メンバー削除.
            //
            (obj as IDictionary<string, object>).Remove("Age");
            
            //
            // 値変更.
            //
            obj.Name = "gsf_zero2";
            
            //
            // 実行結果:
            //       Property Changed:Name
            //       Property Changed:Age
            //       Property Changed:Age
            //       Property Changed:Name
            //
        }
    }


参考にしたリソースは以下の通りです。
てか、ほとんどMSDNのままですが・・・w


今回の記事では、全然記述してませんが、DynamicObjectというのもあります。
DynamicObjectについては、別の記事で書こうと思います。


2番目のリソースにて、紹介されているElasticObjectは、目から鱗でした。



================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。