DLR で名前付き引数を使う

C# で、メソッドに引数として「キーと値のペア」を渡す方法を考えてみます。
例えば HTTP で GET でアクセスするときに URL でクエリ文字列を指定する場合が挙げられます。

よく使われているのは、メソッドの引数に Dictionary や匿名型オブジェクトを渡す方法かと思います。
以下の HttpHelper クラスのように実装します。
なお、WebClient クラスではクエリ文字列 (QueryString プロパティ) は NameValueCollection 型であるため、
受け取った情報を NameValueCollection 型に変換しています。

なお、ここでは題材として CGI’s 郵便番号検索 API を利用しています。

さて、動的言語ランタイム (DLR)名前付き引数を利用して、引数の情報を実行時に解決できないかと考えると、
次のような方法を思いつきます。

dynamic http = new DynamicHttpProxy();
var result = http.Get(Uri_Cgis_Xml, zn: "402", ver: 1);

実際、DynamicObject クラスを継承した DynamicHttpProxy クラスを次のように作れば可能です。

TryInvokeMember メソッドの中で、引数の名前は binder.CallInfo.ArgumentNames で取得できます。
ただし、引数の名前を指定せずに渡された分はここに含まれない (コレクションの長さが変わる) ため注意が必要です。

 

また、C# 7.0 で追加された ValueTuple を利用して、

var result = HttpHelper.Get(Uri_Cgis_Xml, (zn: "6050073"));

とする案もありましたが、

  • 要素が 1 つ以下の場合、タプル リテラルを記述できない
  • コンパイル後はフィールド名が残らないため、実行時に動的に取得できない

という制約により実現できませんでした。

次回:インターフェイスに対する透過プロキシ

作成したサンプル
DynamicHttpConsole (GitHub)

バージョン情報
C# 7.0
.NET Framework 4.5

参照
動的言語ランタイムの概要
名前付き引数と省略可能な引数
タプル – C# によるプログラミング入門

広告

プロパティ変更をタイマーで同期して通知する

C# のコードでデータ バインディング (1) で書いた通り、
Binding Source は INotifyPropertyChanged か DependencyObject でなければ
プロパティ変更通知ができないため、永続的に値が反映される OneWay バインディングはできません。

そこで、プロパティ変更通知機能を持たない既存のオブジェクトをタイマーで監視して
プロパティ変更通知を発生させる DynamicSyncProxy<T> クラスというのを作ってみました。

DynamicObject クラスを継承し、既存のオブジェクトを内部に持ちます。
そのオブジェクトのプロパティへのアクセスを透過するプロキシとして機能します。

さて、プロパティ変更通知機能を持たない TextModel クラスを用意して、
DynamicSyncProxy<T> を介してデータ バインディングを設定します (dynamic 型として扱います)。

この例では 0.3 秒ごとに値が同期されます。

DynamicBinding

 

前回: ExpandoObject を使ったデータ バインディング
次回: XAML マークアップ拡張を自作する

作成したサンプル
DynamicBindingWpf (GitHub)

バージョン情報
C# 6.0
.NET Framework 4.5

参照
データ バインディングの概要
C# のコードでデータ バインディング (1)

ExpandoObject を使ったデータ バインディング

C# のコードでデータ バインディング (1) では、
INotifyPropertyChanged インターフェイスを実装した Person1 クラスに
プロパティを定義してデータ バインディングを構成しました。

ところで、dynamic 型として扱うことで実行時に任意のプロパティを追加できる ExpandoObject クラスというものがあり、
しかも INotifyPropertyChanged インターフェイスを実装しています。
したがって ExpandoObject クラスを使えば、
プロパティを事前に定義しなくてもデータ バインディングができるようになります。

次のコードは、C# のコードでデータ バインディング (1) の TwoWay バインディングのコードを少し書き換えて、
ExpandoObject クラスを使うようにしたものです。
Person1 クラスを使ったときと同様にデータ バインディングが動作します。

 

さて、ExpandoObject は、XAML で DataContext に設定した場合でもプロパティにアクセスできます。
次に示す XAML では、空の ExpandoObject オブジェクトに対して、
UI コントロールから Input プロパティと Output プロパティに直接バインドしています。

コード側で、Input を大文字に変換して Output に反映させています。

ExpandoBinding

 

前回: C# のコードでデータ バインディング (3)
次回: プロパティ変更をタイマーで同期して通知する

作成したサンプル
BindingConsole (GitHub)
ExpandoBindingWpf (GitHub)

バージョン情報
C# 6.0
.NET Framework 4.5

参照
データ バインディングの概要
C# のコードでデータ バインディング (1)