この質問に見られるように: 拡張メソッドを使用してC#イベントを発生させる-それは悪いですか?
この拡張メソッドを使用してイベントを安全に発生させることを考えています。
public static void SafeRaise(this EventHandler handler, object sender, EventArgs e)
{
if (handler != null)
handler(sender, e);
}
しかし、Mike Rosenblumは、JonSkeetの回答でこの懸念を提起しています。
[MethodImpl(MethodImplOptions.NoInlining)]属性をこれらの拡張メソッドに追加する必要があります。そうしないと、デリゲートを一時変数にコピーする試みがJITterによって最適化され、null参照例外が発生する可能性があります。
拡張メソッドがNoInliningでマークされていない場合に競合状態が発生するかどうかを確認するために、リリースモードでいくつかのテストを行いました。
int n;
EventHandler myListener = (sender, e) => { n = 1; };
EventHandler myEvent = null;
Thread t1 = new Thread(() =>
{
while (true)
{
//This could cause a NullReferenceException
//In fact it will only cause an exception in:
// debug x86, debug x64 and release x86
//why doesn't it throw in release x64?
//if (myEvent != null)
// myEvent(null, EventArgs.Empty);
myEvent.SafeRaise(null, EventArgs.Empty);
}
});
Thread t2 = new Thread(() =>
{
while (true)
{
myEvent += myListener;
myEvent -= myListener;
}
});
t1.Start();
t2.Start();
リリースモードでしばらくテストを実行しましたが、NullReferenceExceptionが発生することはありませんでした。
それで、マイク・ローゼンブラムは彼のコメントで間違っていましたか、そしてメソッドのインライン化は競合状態を引き起こすことができませんか?
実際、本当の問題は、SaifeRaiseが次のようにインライン化されるかどうかだと思います。
while (true)
{
EventHandler handler = myEvent;
if (handler != null)
handler(null, EventArgs.Empty);
}
また
while (true)
{
if (myEvent != null)
myEvent(null, EventArgs.Empty);
}