連載:C# 4入門

第8回 ExpandoObjectを継承した動的拡張できるクラス

株式会社ピーデー 川俣 晶
2011/02/10
Page1 Page2 Page3

値の列挙

 実行時に動的にいくらでもメンバを追加できるというと、「いま何が含まれているのか」が気になるところだ。

 ExpandoObjectに含まれる内容の一覧は簡単に取得できる。

using System;
using System.Collections.Generic;
using System.Dynamic;

class Program
{
  static void Main(string[] args)
  {
    dynamic x = new ExpandoObject();
    x.A = 1;
    x.B = 2.0m;
    x.C = 3.0d;
    x.D = "ExpandoObject Sample";

    foreach (KeyValuePair<String, Object> a in x)
    {
      Console.WriteLine("{0}={1} ({2})",
                          a.Key, a.Value, a.Value.GetType().Name);
    }
  }
}
リスト3

A=1 (Int32)
B=2.0 (Decimal)
C=3 (Double)
D=ExpandoObject Sample (String)
リスト3の実行結果

 ポイントはforeach文で、個々の値をKeyValuePair<String, Object>型で受けることだ。これに、キーと値のペアが入るので、名前とその値を調べることができる。もちろん、値の型を調べることも普通にできる。

 ちなみに、このようなコードが可能であるのは、ExpandoObjectクラスがIEnumerable<KeyValuePair<string, Object>>インターフェイスを実装しているからである。

値のチェック

 すでに値が入っているときだけ読み出したい、と思うなら列挙は過剰に強力すぎる。具体的に特定のメンバの有無だけが分かればよい。その方法は用意されている。

using System;
using System.Collections.Generic;
using System.Dynamic;

class Program
{
  private static void showAplha(dynamic x)
  {
    if (((IDictionary<String, Object>)x).ContainsKey("Aplha"))
      Console.WriteLine("Alpha={0}", x.Aplha);
    else
      Console.WriteLine("No Member Alpha here.");
  }

  static void Main(string[] args)
  {
    dynamic x = new ExpandoObject();
    showAplha(x);

    x.Aplha = "Omega";
    showAplha(x);
  }
}
リスト4

No Member Alpha here.
Alpha=Omega
リスト4の実行結果

 ここでのポイントは、ExpandoObjectはIDictionary<String, Object>インターフェイスを実装しているという点である。しかしキャストしないと、このインターフェイスのメソッドにアクセスできない。そのため、このコードではif文の中でキャストを行っている。これで、ContainsKeyメソッドを利用できるようになる。

LINQでクエリする

 「列挙できるものはクエリできるもの」という原則からすると、列挙できると分かればLINQでクエリできそうに思える。しかし、これは一筋縄ではいかない。なぜなら、ExpandoObjectは通常dynamic型を経由してアクセスするが、dynamic型はクエリできないからだ。

using System;
using System.Linq;
using System.Dynamic;

class Program
{
  static void Main(string[] args)
  {
    dynamic x = new ExpandoObject();
    x.Aplha = 1;
    x.Beta = 2;
    x.Gamma = 3;
    foreach (var v
            in ((ExpandoObject)x).Where((n) => (int)n.Value >= 2))
    {
      Console.WriteLine("{0}={1} ({2})",
                         v.Key, v.Value, v.Value.GetType().Name);
    }
  }
}
リスト5

Beta=2 (Int32)
Gamma=3 (Int32)
リスト5の実行結果

 解決策は簡単である。変数xをdynamic型から本来のExpandoObject型へキャストして戻すだけである。

 あるいは以下のような書き換えでもよい。

foreach (var v in
  ((IEnumerable<KeyValuePair<String, Object>>)x).Where((n)
                                          => (int)n.Value >= 2))

 しかし、以下の書き換えはコンパイルできるが、実行はできない。例外が起きる。

foreach (var v in x.Where(
  (Func<KeyValuePair<String, Object>,bool>)((n)
                                          => (int)n.Value >= 2)))

 ExpandoObjectは、IEnumerable<KeyValuePair<String, Object>>インターフェイスを実装しているのでWhereメソッドが使用できるが、ExpandoObject自身は直接これを外部に提供していないからである。IEnumerable<KeyValuePair<String, Object>>型を経由したときだけ利用できる。

メンバの削除

 もういらない値が永遠に残り続けるのも無駄である。しかし、名前を指定して代入するとメンバが自動的に生成される機能性だけでは削除できない。この場合は明示的に削除できる。

using System;
using System.Collections.Generic;
using System.Dynamic;

class Program
{
  private static void showAplha(dynamic x)
  {
    if (((IDictionary<String, Object>)x).ContainsKey("Aplha"))
      Console.WriteLine("Alpha={0}", x.Aplha);
    else
      Console.WriteLine("No Member Alpha here.");
  }

  static void Main(string[] args)
  {
    dynamic x = new ExpandoObject();
    x.Aplha = "Omega";
    showAplha(x);
    ((IDictionary<String, Object>)x).Remove("Aplha");
    showAplha(x);
  }
}
リスト6

Alpha=Omega
No Member Alpha here.
リスト6の実行結果

 削除の機能性はIDictionary<String, Object>インターフェイスにあるので、やはりキャストしてRemoveメソッドを呼び出すだけである。


 INDEX
  C# 4入門
  第8回 ExpandoObjectを継承した動的拡張できるクラス
    1.このコードは是か非か?
  2.値の列挙/値のチェック/LINQでクエリする/メンバの削除
    3.メソッド・モドキの追加/まとめ
 
インデックス・ページヘ  「C# 4入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間