Initialize vs New and object instantiation

Initialize vs New and object instantiation

Old forum URL: forums.lhotka.net/forums/t/2408.aspx


jwooley posted on Thursday, February 22, 2007

I have been working on migrating from WithEvents to explicitly wiring up my event handlers in order to handle issues with events crossing the DataPortal. As mentioned in other threads, I found the need to hook the events OnDeserialized. As a result, I have found it convient to use a common HookEvents method to call both when I am creating the object and when it is returned from the DataPortal. Naturally, I don't want to hook the event to an object that has not been instantiated yet. Here is where I ran into an unexpected issue with the timing of Initialize and New when declaring private fields using the "Private _foo as New Foo" syntax.

To demonstrate, I created a couple quick classes: a person and a dog. (The sample classes follow below). The Person contains a Dog. The person can Scold (public method) a Dog which causes the dog to Bark (public property). When the dog starts barking, (PropertyHasChanged), the Person starts to hear the dog bark (Dog_PropertyChanged). After the person has heard the dog enough (Threading.Sleep), she Pets (public method) the dog causing it to stop barking.

Public Class Person
  
Inherits Csla.BusinessBase(Of Person)
  
Private _Dog As New Dog
  
Public ReadOnly Property Dog()
     
Get
        
Return _Dog
     
End Get
  
End Property

   'We can't hook the events here as _Dog is not yet initialized
   'Protected Overrides Sub Initialize()
  
' MyBase.Initialize()
  
' HookEvents()
  
'End Sub  

   Public Sub New()
     
MyBase.New()
      '_Dog is now instantiated, we can safely hook the events
     
HookEvents()
  
End Sub

   Private Sub HookEvents()
     
If Not _Dog Is Nothing Then
        
AddHandler _Dog.PropertyChanged, AddressOf Dog_PropertyChanged
    
End If
  
End Sub

   Private Sub Dog_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
     
If _Dog.Barking Then
       
System.Threading.Thread.Sleep(1000)
       
_Dog.Pet()
     
End If
  
End Sub

   Protected Overrides Function GetIdValue() As Object
     
Return 2
  
End Function

End Class

Public Class Dog
  
Inherits Csla.BusinessBase(Of Dog)

   Protected Overrides Function GetIdValue() As Object
     
Return 1
  
End Function

   Public Sub Scold()
      Console.WriteLine(
"Scolding")
      Barking =
True
  
End Sub

   Public Sub Pet()
      Console.WriteLine(
"Petting")
      Barking =
False
  
End Sub

   Private _Barking As Boolean
  
Public Property Barking() As Boolean
    
Get
        
Return _Barking
     
End Get
     
Private Set(ByVal value As Boolean)
        
If _Barking <> value Then
           
_Barking = value
            PropertyHasChanged(
"Barking")
        
End If
     
End Set
 
End Property

End Class

Module Module1
  
Sub Main()
     
Dim p As New Person
      Console.WriteLine(
"Person Angry")
      p.Dog.scold()
      Console.WriteLine(
"Is Barking:" & p.Dog.barking.ToString)
      Console.ReadLine()
  
End Sub
End
Module

What I discovered is if I try to hook the events in the Initialize, the private field (_Dog) is not yet initialized and thus the event handler is never added). To test this, comment out the constructor for Person and uncomment the "Initialize" method. You will see that the final "Is Barking" value is still true (because the person never heard the property changed notification.)  Instead, hook up events in the constructor (New) not the initializer (Initialize). If you are utilizing Initialize, make sure you are not accessing any fields that are not yet set. Considering this, I'm not sure the viability of the "Initialize" overload method in business tier code.

Jim Wooley
http://devauthority.com/blogs/jwooley

xal replied on Thursday, February 22, 2007

C# behaves differently in this scenario... I'm a vb guy myself, so it's sad to know that. Big Smile [:D]

Anyway, what you're saying is absolutely true, but it doesn't mean that Initialize shouldn't be used.
As the method's name implies, it's meant for you to do initializtion, not only handle events, but possibly resolve this problem.

So, if you ignore the fact that you loose the flexibility to instantiate stuff upon declararation, you could do:

   Private _Dog As Dog

   Protected Overrides Sub Initialize()
    MyBase.Initialize()
    _Dog = New Dog()
    HookEvents()
   End Sub 


So you could either do that, or move to c# Big Smile [:D]

Andrés

jwooley replied on Thursday, February 22, 2007

Andres,

Thank you for the clarification. I did test this in C# and indeed the code works properly with either the constructor or Initialize implementation. The difference makes it all the more frustrating. Hopefully this will help someone out there not be caught off-guard.

Jim

Copyright (c) Marimer LLC