RaiseEvent Statement not notifiying event handlers when called from within New Sub
Hello -
The following issue involves code from MSDN which does not work properly. I have tried the code in VS 2005 SP1 and VS 2003. Here is the msdn link.http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbls7/html/vblrfVBSpec8_6_1.asp
The problem is that the Constructed Event Handler does not fire when RaiseEvent Constructed is called from the Raiser Class's Sub New (See Following Code):
Imports SystemClass Raiser
Public Event Constructed(ByVal Count As Integer)
Public Sub New()
Static CreationCount As Integer = 0
CreationCount += 1
RaiseEvent Constructed(CreationCount)
End Sub
End Class
Module Test
Private WithEvents x As Raiser
Private Sub Constructed(ByVal Count As Integer) Handles x.Constructed
Console.WriteLine("Constructed instance #" & Count)
End Sub
Public Sub Main()
x = New Raiser ' Causes "Constructed instance #1" to be printed.
x = New Raiser ' Causes "Constructed instance #2" to be printed.
x = New Raiser ' Causes "Constructed instance #3" to be printed.
End Sub
End Module
[1477 byte] By [
Jazoned] at [2007-12-28]
Hmm. Interesting. The event should be declared as shared, however, according to the RaiseEvent documentation.
http://msdn2.microsoft.com/en-us/library/fwd3bwed.aspx
"Non-shared events should not be raised within the constructor of the class in which they are declared. Although such events do not cause run-time errors, they may fail to be caught by associated event handlers. Use the Shared modifier to create a shared event if you need to raise an event from a constructor."
Even so, I tested this and only got two of the three events to fire - the initial constructor did not raise the event, but the subsequent ones did. It sounds like a bug. Perhaps someone else could chime in. Here's some slightly modified code,that replicates the problem:
Module Module1
Private WithEvents x As Raiser
Sub Main()
x = New Raiser ' No Event Fires
x = New Raiser
x = New Raiser
Console.ReadKey()
End Sub
Private Sub x_Constructed(ByVal instance As Integer) Handles x.Constructed
Console.WriteLine("Event Constructed #" & instance.ToString)
End Sub
End Module
Public Class Raiser
Public Shared Event Constructed(ByVal instance As Integer)
Private Shared instance As Integer
Public Sub New()
instance += 1
Console.WriteLine("Constructor #" & instance.ToString)
RaiseEvent Constructed(instance)
End Sub
End Class
The result displayed in the console is (the Event Constructed #1 never fires):
Constructor #1
Constructor #2
Event Constructed #2
Constructor #3
Event Constructed #3
Hi
If you think about what is happening here, it makes sense .....
The WithEvents keyword is some syntactical sugar that removes the need for you to manually attach delegates. Until variable x has a reference to an object, the compiler cannot hook up the events thus if you raise events in a constructor, the handler does not yet exist.
Shared events could theoretically be raised from a constructor as someone could attach a handler to it before instantiating the class instance.
eg
private x as raiser
addhandler raiser.constructed, addressof me.x_constructed
x = new raiser
Hope this helps to clear up what is happening ... never trust the documentation :)
Richard
Thats what I believe is happening also. To 'alleviate' this problem (apart from not raising events in a constructor, which I've never done before), I'd performed the following (which is what you stated), Richard:
Module Module1
Private x As Raiser ' NOT With Events
Sub Main()
AddHandler Raiser.Constructed, AddressOf x_Constructed
x = New Raiser ' Event should fire correctly
x = New Raiser
x = New Raiser
Console.ReadKey()
End Sub
Private Sub x_Constructed(ByVal instance As Integer) 'Handles x.Constructed
Console.WriteLine("Event Constructed #" & instance.ToString)
End Sub
End Module
Public Class Raiser
Public Shared Event Constructed(ByVal instance As Integer)
Private Shared instance As Integer
Public Sub New()
instance += 1
Console.WriteLine("Constructor #" & instance.ToString)
RaiseEvent Constructed(instance)
End Sub
End Class
In this case, the attached handler fires OK, and all messages get shown. Of course, never trust the documentation, as you say
. The critical thing is that one needs to understand more than how to plug in code. There's a lot which goes on behind the scenes which VB (still!) hides from the user.
Based on what you say, Richard, I would see two things as options for solutions to this issue:
1. Don't raise events in constructors :)
2. Attach a handler to the shared event prior to instantiating the object.
Also, I'd probably change the pattern of the event to follow the .NET framework default event handling, since the same event would be raised for multiple objects you may need a way to distinguish between those objects (add a sender as object parameter).