DataGrid, 脱Reflection(参)

PropertyDescriptorのGetValue, SetValueメソッドを実装する。内部として、リフレクションではなく、式木を使う。
○式木を使って、プロパティからの取得や設定を行うメソッド(FuncやAction)を生成する。
まず、Get用。
using System.Linq.Expressions;
...
/// <summary>
/// 引数オブジェクトから指定プロパティの値を取得する為の
/// メソッド(Func)を生成します。<br/>
/// </summary>
/// <param name="component">オブジェクト</param>
/// <returns>プロパティの値を取得する為のメソッド(Func)</returns>
private Func<object, object> CreateGetter(object component) {
//
var target = Expression.Parameter(typeof(object), "obj");

//Convert(obj).${propName}
var propExpression =
Expression.Property(
Expression.Convert(target, component.GetType()),
this.propertyInfo_.Name);

//obj => Convert(Convert(obj).${propName})
//Convert(obj) でobject型から具体的な型にConvert
//更にConvertを行うのは、そうしないと、値型をobjectに変換できない為
var expr = Expression.Lambda<Func<object, object>>(
Expression.Convert(propExpression,typeof(object)),
target);

var getter = expr.Compile();
return getter;
}

次にSet用。
/// <summary>
/// 引数オブジェクトのプロパティへ値を設定する為のメソッド(Action)を
/// 生成します。、
/// </summary>
/// <param name="component">オブジェクト</param>
/// <returns>プロパティへ値を設定する為のメソッド(Action)</returns>
private Action<object, object> CreateSetter(object component) {
var target = Expression.Parameter(typeof(object), "obj");
var valueToSet = Expression.Parameter(typeof(object), "value");

//(obj, value) => (Convert(obj).${propName} = Convert(value))
//というラムダ式なメソッドを生成します。
var expr = Expression.Lambda<Action<object, object>>(
Expression.Assign(
Expression.Property(
Expression.Convert(target, component.GetType()),
this.propertyInfo_.Name),
Expression.Convert(valueToSet,
this.propertyInfo_.PropertyType)),
target, valueToSet);

return expr.Compile();
}
式木で何をしているか?というと、プロパティから値を取得するラムダ式、或いはプロパティへ値を設定するラムダ式を動的に作っているという事になるのかな。
これらのメソッドをGetValueやSetValueに組み込む。
/// <summary>プロパティの値を取得するFunc</summary>
private static Func<object, object> getProperty_;

///// <summary>ロック用オブジェクト</summary>
private static readonly object lockObj_ = new object();

/// <summary>
/// コンポーネントのプロパティの現在の値を取得します。
/// </summary>
/// <param name="component">コンポーネント</param>
/// <returns>値</returns>
public override object GetValue(object component) {
//ここが肝心!
//当クラスが対象とするプロパティから値を取得して返します。
if (TwVMPropertyDescriptorthis.getProperty_ == null) {
lock (lockObj_) {
TwVMPropertyDescriptorthis.getProperty_ =
this.CreateGetter(component);
}
}

return TwVMPropertyDescriptor.getProperty_(component);
}
lockObj_は非同期的に呼ばれた時の対策として用意されたロック用のオブジェクト。CreateGetterメソッドで生成したメソッドは、static変数として保持しておく。これは逐一コンパイルすると返ってパフォーマンスが悪くなる為。訂正。保持するなら、staticなディクショナリで持たせないと正常に動作しない。
/// <summary>プロパティに値を設定するAction</summary>
private static Action<object, object> setProperty_;

/// <summary>
/// コンポーネントの値を別の値に設定します。
/// </summary>
/// <param name="component">コンポーネント</param>
/// <param name="value">値</param>
public override void SetValue(object component, object value) {
//ここが肝心!
//当クラスが対象とするプロパティへ引数の値(value)を設定します。
//今現在の値を取得します。
var orgValue = this.GetValue(component);

if (orgValue == null || value == null) {
//お互いnull
return;

} else if (orgValue != null && value != null &&
orgValue.Equals(value)) {
//値的に等しい
//object型で扱っている以上、==での比較はできない
return;
}

if (TwVMPropertyDescriptorthis.setProperty_ == null) {
lock (lockObj_) {
TwVMPropertyDescriptorthis.setProperty_ =
this.CreateSetter(component);
}
}

TwVMPropertyDescriptorthis.setProperty_(component, value);

//値の変更を通知します。
OnValueChanged(component,
new PropertyChangedEventArgs(this.propertyInfo_.Name));
return;
}
CreateSetterで作ったActionオブジェクトもstatic変数に確保する。これで実装はほぼ終了したので、使って見ることにする。
/// <summary>
/// お試しクラス
/// </summary>
public class DataObject {

/// <summary>
/// Dataプロパティの値を取得|設定します
/// </summary>
public decimal Data {
get; set
}
}

//Dataプロパティの(メタ)情報を首都医します。
var propInfo =
typeof(DataObject).GetProperty("Data",
BindingFlags.Public | BindingFlags.Instance);
//Dataプロパティ記述子を生成します。
TwVMPropertyDescriptor descriptor =
new TwVMPropertyDescriptor(propInfo);
//試し用のインスタンス
var dataObj = new DataObject {Data = decimal.One};

//値を取得します。
var val = descriptor.GetValue(dataObj);
//値を設定します。
descriptor.SetValue(dataObj, 123M);
dataObjというインスタンスが目の前にあるのに、態々PropertyDescriptor経由で取得とか設定とかって、ぶっちゃけ意味はない。あくまで動作確認用。これがちゃんと動けば、後2つのクラスを作るだけとなる。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m