nugetのほうにアップしてあります。
今回のは機能追加ではなくて性能改善です。
記述の簡潔さに力を入れてリフレクションを裏で振り回してたのをExpression Treeを組み立ててコンパイルしたものをキャッシュするように改造してみました。
ただ、この改造はWPFのみでSilverlight版は相変わらずリフレクションでやってますorz
実行時エラーがとれませんでした。
どれくらい改善されたか
前回測定したときの値は以下のようなものでした。(100個のコマンドがあるViewModelを100個作るのにかかる時間)
Kinkuma Framework 1.2.5 | 2000ms〜3000ms |
Kinkuma Framework + T4 | 300ms〜400ms |
手書き | 10ms〜20ms |
今回のバージョンでは・・・
Kinkuma Framework 1.2.7(初回) | 380ms |
Kinkuma Framework 1.2.7(二回目以降) | 200ms〜250ms |
劇的に早くなりました!初回は、初期化のための式木を動的に組み立ててコンパイルしてるので若干遅いですが、二度目からはコンパイル済みのものを再利用するので若干早くなります。これなら実用的な速度かな??
記述方法
Kinkuma FrameworkのViewModelの記述方法をちょいと説明します。WPFにしか対応していませんが、T4テンプレートを使ったViewModelのアイテムテンプレートを提供しているので、それを使った書き方を説明します。T4テンプレート側にプロパティの定義を行います。
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".generated.cs" #> <#@ include file="$(ProjectDir)\Kinkuma\MVVMParts.ttinclude" #> // <generated /> namespace TestApp { using System; [System.ComponentModel.TypeDescriptionProvider(typeof(SampleViewModelAssociatedMetadataTypeTypeDescriptionProvider))] public partial class SampleViewModel { <# // string型のNameプロパティ Property("string", "Name"); #> <# AssociatedMetadataTypeTypeDescriptionProvider("SampleViewModel"); #> } }
これで以下のような変更通知つきのプロパティが作成されます。
// <generated /> namespace TestApp { using System; [System.ComponentModel.TypeDescriptionProvider(typeof(SampleViewModelAssociatedMetadataTypeTypeDescriptionProvider))] public partial class SampleViewModel { #region "Nameプロパティ" private string _Name; partial void NameChanging(string newValue, ref bool canSetValue); partial void NameChanged(); public string Name { get { return this._Name; } set { if (this._Name == value) { return; } bool canSetValue = true; this.NameChanging(value, ref canSetValue); if (!canSetValue) { return; } this._Name = value; this.RaisePropertyChanged("Name"); this.NameChanged(); } } #endregion class SampleViewModelAssociatedMetadataTypeTypeDescriptionProvider : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider { public SampleViewModelAssociatedMetadataTypeTypeDescriptionProvider() : base(typeof(SampleViewModel)) { } } } }
そして、C#のクラス側ではコマンドやInteractionRequestを定義したりします。その時の記述も個人的にめんどくさいと思っていたコードを、なるべく属性指定で済むようにしています。
namespace TestApp { using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; using Microsoft.Practices.Prism.Commands; using Microsoft.Practices.Prism.Interactivity.InteractionRequest; using Okazuki.MVVM.PrismSupport.Utils; using Okazuki.MVVM.PrismSupport.ViewModels; [MetadataType(typeof(Metadata))] public partial class SampleViewModel : ViewModelBase { // AutoInit属性をつけると自動的にインスタンスが作成される [AutoInit] public ObservableCollection<Person> People { get; private set; } // 自動プロパティでのインスタンスの初期化が可能なんです。 [AutoInit] public InteractionRequest<Notification> SampleRequest { get; private set; } // コマンドはXXXXCommand, XXXXExecute, CanXXXXExecuteという命名規約で // コマンドのプロパティとメソッドを作成して属性をつけることで自動的に // 初期化が行われる。 [AutoInitCommand] public DelegateCommand SampleCommand { get; private set; } [CommandMethod] private void SampleExecute() { // コマンドの実行メソッド } [CommandMethod] private bool CanSampleExecute() { // コマンドの実行可否判定 return true; } class Metadata { // T4で作成したプロパティにDataAnnotationをつけるにはメタデータクラスを使う [Required(ErrorMessage = "名前を入力してください")] public object Name { get; set; } } } class Person{} }
AutoInit属性でデフォルトのインスタンスをプロパティに設定してAutoInitCommand属性でコマンドの自動初期化を行います。速度も実用的になったし結構いい感じかも。