Brad Abrams had a recent post about how the usual way of raising an event in C#:
protected virtual void OnMyEvent(EventArgs e)
{
if(MyEvent != null)
{
MyEvent(this, e);
}
}
is not threadsafe. Purely out of curiosity, I wondered if VB handles this the correct way:
protected virtual void OnMyEvent(EventArgs e)
{
EventHandler handler = MyEvent;
if(handler != null)
{
handler(this, e);
}
}
Since VB abstracts this away with the RaiseEvent keyword. Unfortunately it doesn’t seem to, as this code:
Protected Overridable Sub OnMyEvent(ByVal e As EventArgs)
RaiseEvent MyEvent(Me, e)
End Sub
Compiles to this:
.method family newslot virtual instance void OnMyEvent([mscorlib]System.EventArgs e) cil managed
{
// Code Size: 22 byte(s)
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
L_0006: brfalse.s L_0015
L_0008: ldarg.0
L_0009: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
L_000e: ldarg.0
L_000f: ldarg.1
L_0010: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, [mscorlib]System.EventArgs)
L_0015: ret
}
Which has a race condition–it should store the delegate in a local. Generally, my answer to all of these questions is “it is fixed in 2005” – so I’m going to just assume that that is the case here.
Looks like it is
November CTP:
Public Event SomeEvent()
Public Sub IsItFixed()
RaiseEvent SomeEvent()
End Sub
.method public instance void IsItFixed() cil managed
{
// Code size 17 (0×11)
.maxstack 1
.locals init ([0] class WinAppVB.Form1/SomeEventEventHandler VB$t_ref$S0)
IL_0000: ldarg.0
IL_0001: ldfld class WinAppVB.Form1/SomeEventEventHandler WinAppVB.Form1::SomeEventEvent
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0010
IL_000a: ldloc.0
IL_000b: callvirt instance void WinAppVB.Form1/SomeEventEventHandler::Invoke()
IL_0010: ret
} // end of method Form1::IsItFixed