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 System

Class 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]
# 1

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

SJWhiteley at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 2

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

DickDonny at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 3

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).

SJWhiteley at 2007-9-4 > top of Msdn Tech,Visual Basic,Visual Basic Language...