背景
Taskを始めとする別スレッド内でUI Threadに関わる処理を行っているとき、DispatcherObjectの BeginInvoke メソッドを多用します。たとえば、Task内でプログレスバーを更新する場合は、
var pb = new ProgressBar();
pb.Maximum = 5;
var window = new Window() { Content = pb };
// ダメな例:Task内でUIに関わる処理を行うと例外が発生します
Task.Factory.StartNew(()=>{
pb.Value++;
});
// 正しい例:UI にかかわるオブジェクトはDipatcherObjectを継承しているため、Invokeします
Task.Factory.StartNew(() =>
{
pb.Dispatcher.BeginInvoke((Action)(()=> pb.Value++));
}
と記述します。一行で書けるとはいえ、コードが長くなりがちです。
ラムダ式は直接指定できない
InvokeやBeginInvokeの引数はDelegate型のため、Action (Delegateの中の一つ)にキャストせず、ラムダ式を直接指定すると、
// ダメな例:ラムダ式はキャストしなければならない
Task.Factory.StartNew(() =>
{
// ラムダ式 はデリゲート型ではないため、型 'System.Delegate' に変換できません
pb.Dispatcher.BeginInvoke(()=> pb.Value++);
}
というエラーが発生します。そのため、簡単な処理を記述するたびに pb.Dispatcher.BeginInvoke((Action)(()=> {})) と長いコードを書かなければなりませんでした。
拡張メソッド & Actionを引数にした関数
そこで、僕は以下の拡張メソッドを定義し、
// 拡張メソッドはinternalの方が汚染がなくていいと思います
internal static DispatcherOperation DisBegInv(this DispatcherObject dispatcherObject, Action action)
{
return dispatcherObject.Dispatcher.BeginInvoke(action);
}
// 腕にやさしいコード
Task.Factory.StartNew(() =>
{
pb.DisBegInv(()=> pb.Value++));
}
とラムダ式を直接用いるようにしています。これで皆さんの腕が少しでも疲れないようになれば幸いです。