前回:http://blogs.wankuma.com/kazuki/archive/2008/02/08/122068.aspx
前回でネタ切れって言ってたけど、まだあったわ。
DependencyObjectについては「DependencyObject使うと色々な機能が使えるよ~」ってことと「ちょっ!これしきのクラスにこんなに行数かかるの!?」っていう感想を持ってる。
今回は、DependencyObjectを継承すると一体どういう制限があるのかを見ていこうと思う。
といっても言うことはこれだけ。
インスタンスを生成したスレッド以外からDependencyPropertyにアクセス出来ない!
この動きはどうもDependencyObjectの親クラスであるDispatcherObjectから受け継いだものらしい。
早速実験コード。
using System;
using System.Windows;
namespace DependencyObjectThread
{
class Program
{
static void Main(string[] args)
{
DependencyProperty prop = DependencyProperty.Register("Hoge", typeof(string), typeof(Program));
DependencyObject obj = new DependencyObject();
obj.SetValue(prop, "Hello");
Console.WriteLine(obj.GetValue(prop));
}
}
}
適当なDependencyPropertyを作成して、その値をDependencyObjectにセットして取り出して標準出力へ出している。
実行すると「Hello」と表示される。
ここまではとりあえず問題ない。Threadなんて出てきてないから。
じゃぁ、これをベースにobjにセットした値を別スレッドから参照してみるように変えてみる。
using System;
using System.Windows;
using System.Threading;
namespace DependencyObjectThread
{
class Program
{
static void Main(string[] args)
{
DependencyProperty prop = DependencyProperty.Register("Hoge", typeof(string), typeof(Program));
DependencyObject obj = new DependencyObject();
obj.SetValue(prop, "Hello");
// 別スレッドからGetValueしてみる
new Thread((object param) =>
{
DependencyObject depObj = param as DependencyObject;
Console.WriteLine(depObj.GetValue(prop));
}).Start(obj);
}
}
}
これを実行すると例外がおきて終了する。
ハンドルされていない例外: System.InvalidOperationException: このオブジェクトは別
のスレッドに所有されているため、呼び出しスレッドはこのオブジェクトにアクセスでき
ません。
場所 System.Windows.Threading.Dispatcher.VerifyAccess()
場所 System.Windows.DependencyObject.GetValue(DependencyProperty dp)
場所 DependencyObjectThread.Program.<>c__DisplayClass1.<Main>b__0(Object para
m) 場所 C:\Users\Kazuki\AppData\Local\Temporary Projects\DependencyObjectThread\
Program.cs:行 19
場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,
ContextCallback callback, Object state)
場所 System.Threading.ThreadHelper.ThreadStart(Object obj)
スタックトレースを見る限り、GetValueの中でDispatcher.VerifyAccessというメソッドを使って別スレッドからのアクセスか見てるようだ。
この別スレッドからアクセスしたら駄目っていう制約は、WPFのコントロールにもきっちり当てはまる。
ということで、スレッドを使うときは、WindowsFormsの時と同じような対処が必要とのこと。