Building Distributed Applications with Visual Basic .NET Errata File

 

Lasted Edited: 5/2/2002

 

Page 15

First Note: The note incorrectly states that there are “over a thousand classes” in VS .NET. As of RC4 there were just over 6,500 classes in about 70 namespaces.

 

Page 22

2nd Paragraph: There are now over 25 languages targeted to the CLR.

 

Page 24

3rd Paragraph: The paragraph states that operating systems don’t know how to begin executing programs written in MSIL. However, since the release of the book an update to Windows XP has enabled the loader in XP to recognize .NET assemblies.

 

Page 35

2nd full paragraph: This paragraph states that the Dispose method calls the finalize method. Although possible in VB .NET this is not the recommended approach since it doesn’t support C#, see the note below on page 158.

 

Page 37

3rd bullet at the bottom of the page: The second phrase should begin “This includes a public key token” (since the public key is actually 4K and would be too big to put in the manifest efficiently). In the same paragraph the parenthetical remark “(also referred to as strong name)” should be removed. Actually the combination of the version, culture, and shared name information make up the strong name as correctly stated in chapter 5.

 

Page 38

Last paragraph. Since this was written the CLR’s class loader has been modified to not take the compatibility number into account. In other words, the CLR will bind with any version if instructed to do so when the version policy is appied. As a result the first and second paragraphs on page 39 are also inaccurate.

 

Page 41

Note: The .NET Admin Tool is now called “Microsoft .NET Framework Configuration” and can be found in the Administrative Tools group.

 

Page 42

5th Paragraph. The paragraph implies that the GAC is searched last. In reality for shared assemblies (those referenced with a strong name), the GAC is searched first.

Last paragraph. The description of Administrator Policy is out of place here since it was discussed earlier. This paragraph should be disregarded.

 

Page 47

1st Paragraph. As mentioned previously, there are actually over 6,500 classes and 70 namespaces.

 

Page 91

The discussion of ReDim on this page should note that arrays are also immutable and so redimensioning an array in VB actually allocates new arrays which may be inefficient. For these types of dynamic allocation operations you should use an ArrayList and then call its ToArray method to return an array.

 

 

Page 132

In the 2nd to last paragraph the statement is made that consumers of a class must implement the event handlers for a particular object if classes derived from the consumer are to be able to handle them. This statement has caused some confusion.

 

The intended meaning is that if the consumer does not use the WithEvents keyword when declaring the object then of course the derived class will not be able to add the WithEvents keyword at a later time. However, the derived class can still handle the event dynamically using the AddHandler keyword like so:

 

Public Class HasEvent

 

    Public Event MyEvent()

 

    Public Sub EventMethod()

        RaiseEvent MyEvent()

    End Sub

 

End Class

 

Public Class myclass1

 

    Protected e As New HasEvent()

 

End Class

 

Public Class myclass2 : Inherits myclass1

 

    Public Sub New()

        AddHandler e.MyEvent, AddressOf myhandler

        e.EventMethod()

    End Sub

 

    Public Sub myhandler()

        ' Will handle the event raised

    End Sub

End Class

 

 

Pages 158 and 159

The discussion here on the Dispose and Finalize pattern is incomplete at best and does not work with both VB and C#. A more complete discussion follows.

 

A major design pattern that you’ll find in the .NET Framework is that of Dispose and Finalize. Basically, you’ll find this pattern in classes that acquire and hold on to resources such as file and window handles or network and database connections. This is the case since the Common Language Runtime (CLR) within which your .NET code runs implements non-deterministic finalization, meaning that the CLR ultimately decides when object instances are no longer able to be referenced and when those instances are deallocated in memory. The process of reclaiming memory by deallocating instances is referred to as garbage collection or simply GC. This approach is obviously much different than in previous versions of VB where setting an object to Nothing deallocated the object immediately causing its Terminate event to run or in C++ where an object could be deallocated at any time causing its destructor to run. Obviously, objects that hold onto resources such as file handles or database connections need a way to release those resources in a timely fashion, hence the dispose and finalize design pattern.

 

The idea behind this pattern is that the client decides when the resources are no longer required and calls the Dispose or Close method on the class. This method then cleans up the resources and ensures that the object can no longer be used. However, adding a Dispose or Close method is not sufficient in and of itself since if the client forgets to call it, the resource may not then be released causing a leak in your application. To ensure that resources are cleaned up, your classes can implement a destructor that gets called automatically by the CLR when the GC process finally cleans up the instance. As a result, in your classes you’ll want to implement a Dispose or Close method along with a destructor as shown here.

 

Public MustInherit Class QuilogyDataAccess : Implements IDisposable

 

  Protected _disposed As Boolean = False

 

  Protected Overridable Sub Dispose(ByVal disposing As Boolean)

    If disposing Then

      ' Call dispose on any objects referenced by this object

    End If

    ' Release unmanaged resources

  End Sub

 

  Protected Overridable Sub Dispose() Implements IDisposable.Dispose

     Dispose(True)

     _disposed = True

    ' Take off finalization queue

    GC.SuppressFinalize(Me)

  End Sub

 

  Protected Overrides Sub Finalize()   

    Me.Dispose(False)

  End Sub

 

End Class

 

 

Public Class Customers : Inherits QuilogyDataAccess

 

  Public Sub Close()

    Me.Dispose(True)

  End Sub

 

  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

    If disposing Then

      ' Call dispose on any objects referenced by this object

    End If

    ' Release unmanaged resources

 

    MyBase.Dispose(disposing)

  End Sub

 

  Public Sub GetCustomers()

    ' Make sure the object has not been disposed

    If MyBase._disposed Then

      Throw New ObjectDisposedException("Customers", "Has been closed")

    End If

 

    ' Do other work here

  End Sub

 

End Class

 

You’ll notice that the QuilogyDataAccess class is a base (MustInherit, abstract in C#) class that ostensibly creates and holds on to a managed resource such as a database connection or an unmanaged resource such as a file or window handle. As a result this class exposes a Dispose method by implementing the IDisposable interface and includes a destructor implemented by overriding the Finalize method. You’ll notice that when the Dispose method inherited from the IDisposable interface is called it calls a second Dispose method passing it True. This second Dispose method cleans up the managed resources by calling their Dispose methods if passed True and then proceeds to deallocate unmanaged resources. When it is finished it returns and the original Dispose method then sets the protected disposed flag to True to indicate that the class has already been disposed. It then calls the static SuppressFinalize method of the GC class. This is done to prevent the Finalize method (the destructor) from running when the GC process next runs. Calling SuppressFinalize is simply an optimization that allows the instance to ultimately be deallocated sooner since all objects with a destructor are placed in a special queue that must first run their destructors before deallocating them.

 

While it may seem strange that you need to make this differentiation, it is important because in the event that the GC process ends up running the Finalize method you have no way of knowing whether or not the managed objects referenced by the class have already been finalized. This is the case since the order of finalization is not deterministic regardless of the order in which the objects were deemed unreachable by the GC. As a result you wouldn’t want to call their Dispose methods if they had already been deallocated. You’ll notice that the Finalize method simply executes the Dispose method passing in False to ensure that unmanaged resources are cleaned up in the event the Dispose method is never executed and the Finalize method is being run by the GC process. Incidentally, the pattern shown above works with C# as well where the destructor is identified using a ~ such as:

 

~ QuilogyDataAccess()

{

  this.Dispose(false);

}

 

The other difference between C# and VB .NET is that destructors in C# can only be called by the GC process and not directly by client code whereas the Finalize method in VB .NET can be called by other code in the class or its descendants. This is the reason that the destructor calls Dispose and not vice versa.

 

The Customers class is then derived from QuilogyDataAccess and can override the Dispose method in order to clean up its own resources by calling Dispose on any objects it references before calling the Dispose method of the base class. As with the base class the Dispose method of the derived class accepts an argument that determines whether managed resources are cleaned up in addition to unmanaged resources. By passing True into the method, as is done by the Close method, the class will call the Dispose methods of any managed objects it references in addition to deallocating unmanaged resources such as file and window handles.

 

You’ll notice that the Customers class also exposes a public Close method that allows clients to clean up the resources explicitly. The Close method simply calls the Dispose method to do the work and can be used in cases where the Close method makes more sense semantically. The GetCustomers method of the Customers class shows how a method can then check to see if the object has previously been disposed before allowing execution to continue. If the object has already been disposed you can throw the ObjectDisposedException. Note that if you wanted to recreate the resources in the event a method like GetCustomers is called after the object has been disposed you would also need to call the ReRegisterForFinalize method of the GC class to make sure the instance will be finalized by the GC eventually in the event that Close or Dispose is not called.

 

Clients using the Customers class can then instantiate the class and work with it as normal, making sure to call the Close method when finished or casting to the IDisposable interface like so.

 

Dim c As New Customers()

 

' Do other work here

c.GetCustomers()

c.Close()

 

Or

 

Dim c As New Customers()

Dim d As IDisposable

 

' Do other work here

c.GetCustomers()

 

d = c

d.Dispose()

 

Alternatively, the C# language includes the using statement that automatically casts to the IDisposable interface and calls the Dispose method when the block is exited like so.

 

using (Customers c = new Customers()) {

  // Do other work here

  c.GetCustomers();

}

 
It should be mentioned that the compiler will only allow the using statement when the class to which it refers implements IDisposable interface. 

 

Page 173

Figure 5.2. While this UI element is really cool, it did not make the final cut and so will not be in the released VS .NET. The reasons I’ve heard for this are not very satisfying, especially one that said it was taken out because C# did not have an equivalent. You now need to use the sn.exe utility and the AssembyKeyFile attribute to make and embed shared name information into the assembly.

 

Page 174

1st Paragraph. See the note on page 41.

 

Page 194

2nd full paragraph. See the note on page 41

 

Page 364

The note on the bottom of the page talks about DisposeObject. You can also use the Dispose method on the object instance to clean up the proxy and COM+ context.

 

Page 367

Just as with the previous note, the first paragraph on this page omits mentioning that the Dispose method of the object will also place it back in the pool immediately.

 

Pages 445 and 446

The OutputCache page directive shown in the snippets on these pages should have a % in them as in:

<%@ OutputCache Duration=”60” Location=”Server” %>

 

Page 626

On this page the code snippet that shows handling the event for the Timer object shows the event name as “Tick”. In the RTM the event name was changed to “Elapsed” so the code snippet should be:

 

AddHandler mProcess.Elapsed, AddressOf OnTimer