かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Kinkuma Framework 1.2.7をリリースしてみました

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属性でコマンドの自動初期化を行います。速度も実用的になったし結構いい感じかも。

コードのダウンロード

CodePlexにコードは公開しています。CodePlexではリリースを作成していないのでソースコードから直接ダウンロードしてください。Kinkuma Frameworkのサンプルや、今回のようにリフレクションを使ってたような場所でExpression Treeを使って高速化をするコードの実装例としても参考になると思います。