Chapter 7: Microsoft .NET Framework Enhancements

 

Microsoft Corporation

March 2006

Summary: Examine new features and how to use them to reduce your coding effort in Visual Basic 2005 while increasing your application's usability, security, and maintainability. (8 printed pages)

Introduction

The .Net Framework 2.0 contains many improvements and additions that cover a broad range of functionality. This chapter examines some of these features and demonstrates how you can use them to reduce your coding effort while increasing your application's usability, security, and maintainability.

Application: Base Class Library Enhancements

This application demonstrates many enhancements made to the Base Class Library, such as type improvements, expanded application tracing, typed resources, and generic collections.

New Concepts

The .NET Framework 2.0 includes some powerful additions that can be implemented in any .NET application. You can use these new features to improve application tasks such as converting and validating data, monitoring execution speed, and capturing and logging application events.

Type Enhancements

The .NET Framework basic types, such as System.String and System.DateTime, have always provided many methods for working with data of that particular type. However, some data- manipulation and type-conversion scenarios were not addressed well. For example, the String type's Split method allows you to parse a delimited string into an array. An item is added to the array for every element in the string. If the string has empty elements (that is, two delimiters adjacent to one another), an empty item is added to the array. This is sufficient most of the time, but in some cases you might want to ignore empty elements instead of having them added to the array. A new Split overload has been added to the String type to handle these situations. You can now split a string and provide a second parameter indicating whether or not you want to ignore empty elements. The following code splits a string containing four elements, one of which is empty. A value of true for the second parameter instructs the method to ignore empty elements, so the resulting array contains only three items.

Dim  colorArray()  as  String
Dim  colors  as  String  =  "Red,Blue,,Green"
colorArray  =  colors.Split(separators,  StringSplitOptions.RemoveEmptyEntries)

Another common task is to parse string data into another data type—for example, when a user enters a date and you need to parse that value into a DateTime variable. Previously, the Parse method was the only mechanism available for this. Unfortunately, the Parse method throws an exception if the string cannot be parsed into the appropriate type. This behavior requires that you wrap any call to the Parse ** method inside of a TryCatch block to handle the exception, which can be a performance issue if the application often encounters invalid data. A new method named TryParse has been added that accepts two parameters. The first is the string you want to parse, and the second is the variable the parsed value will be assigned to if the parse is successful. The method returns a Boolean indicating whether or not the parse was successful. This allows you to check the return value to determine whether the assignment variable contains the parsed value. The following code will attempt to parse the specified string. If the parse is successful, it will display a message box with the parsed value. Otherwise, it displays a failure message.

Dim  inputString  as  String
Dim  parsedValue  as  DateTime
Dim  tryResult  as  Boolean

tryResult  =  DateTime.TryParse(inputString,  parsedValue)
If  tryResult  Then
     MessageBox.Show("The  parsed  date  is  "  &  parsedValue.ToString()  &  ".")
Else
     MessageBox.Show("The  parse  failed.  ")
End  If

One of the first operations you commonly need to perform on string data is to determine whether the variable contains a value or is null or empty. This need is so common that a new method named IsNullOrEmpty has been added to the String class. This is a shared member that simply returns a Boolean indicating whether or not the provided string is equal to Nothing or an empty string. Calling IsNullOrEmpty is equivalent to using the IsNothing function and comparing the variable to String.Empty. The following code uses IsNullOrEmpty to verify that a string variable contains data before attempting to parse it:

If  Not  String.IsNullOrEmpty(inputString)  Then
     DateTime.TryParse(inputString,  parsedValue) 
End  If

Generic Collections

The .NET Framework has always provided many powerful collection classes through the System.Collections and System.Collections.Specialized namespaces. These classes, such as ArrayList and Hashtable, allow you to store objects of any type and easily add, remove, and search for items. However, this flexibility comes at the expense of type safety. The built-in collections are not considered type safe because there is no way to implicitly guarantee that all members of the collection are of a particular type. You have to explicitly implement type safety by writing code that enforces your type constraints anytime an item is added to the collection. In addition, anytime you retrieve an item from the collection, you must manually cast the returned object to your desired type. These type-checking and type-conversion routines are typically implemented in custom collection classes that you write as wrappers around one of the framework provided collections. The following code shows a basic custom collection class that performs these tasks for storing Product objects in an ArrayList:

Public  Class  ProductCollection
     Private  myProducts  As  New  ArrayList
     Public  Sub  Add(ByVal  prod  As  Product)
          myProducts.Add(prod) 
     End  Sub
     Public  Function  Item(ByVal  index  As  Integer)  As  Product
          Return  CType(myProducts(index),  Product)
     End  Function
End  Class

The addition of Generic Collections to the .NET Framework allows you to create type-safe collections with very little code. In fact, you can create a fully implemented version of the product collection class just described with one line of code, as will be shown a little later. The .NET Framework provides generic collections through the System.Collections.Generics namespace and includes generic implementations such as List, Dictionary, Stack, Queue, and others. They are called generics because they contain a generic collection implementation. You tell the generic what type you want it to store when you instantiate it. The compiler is then able to create a specific collection class that is optimized for storing the type you specify and has strong- typed members. The following code creates an instance of the System.Collections.Generics.List class that is typed for storing Product objects. The List class is essentially a generic version of the ArrayList.

Dim  products  As  New  System.Collections.Generic.List(Of  Product)

Notice that the syntax for instantiating a generic is a little different than instantiating a nongeneric class. You use the Of clause to provide the data type that the collection will store. Some generics, such as the Dictionary, allow you to specify more than one data type parameter. The Dictionary generic collection needs two data type parameters, one to specify what type to accept for keys and another to specify what type to accept for values. The resulting instance has members that are strong-typed, so you do not need to write any type-checking or conversion code. The following code shows the use of some methods of the generic List created earlier for storing Product objects:

Dim  prod  as  new  Product("Chai  Tea")
Dim  products  As  New  List(Of  Product)

Products.Add(prod)
Products(0).Description  =  "A  great  tasting  tea.  "

Typed Resources

Resource files have always provided a convenient mechanism for packaging application resources such as interface strings and images. However, in previous versions of the .NET Framework, retrieving a resource from a resource file always returned an object that then had to be manually converted to your desired type. Visual Studio .NET 2005 now solves this problem by automatically creating strongly typed resource wrappers whenever you edit a resource file. These wrappers allow you to easily access resources without having to perform casting operations or know the details of how to use the System.Resources.ResourceManager class. Accessing a resource is as easy as:

MainPictureBox.Image = My.Resources.Sunset

Exceptions and Tracing

The .NET Framework provides the System.Exception class for encapsulating error information and classes in the System.Diagnostics namespace for tracing application event information. The Exception class has always provided properties that expose error state, such as Message, StackTrace, and HelpLink. A new member named Data has been added to the exception class, allowing you to attach any other information you feel is relevant to the exception. For example, you might want to attach some values representing application state at the time of the exception.

The System.Diagnostics namespace provides classes for outputting application event information to an external store such as a file or the Event Log. The .NET Framework 2.0 adds the TraceSource class to provide you with more flexibility over how event data is traced. Each TraceSource instance has its own name and collection of TraceListeners. A trace listener is responsible for sending a trace message to the external store. Being able to define multiple trace sources allows a single application to define individual sources for various parts of the application. For example, you could have one TraceSource for the user interface and another for the data access components.

The System.Diagnostics namespace also includes a new class named StopWatch for easily capturing elapsed intervals. You can start a new timer by calling the shared StartNew method, and you can retrieve elapsed intervals by reading the Elapsed property, which returns a TimeSpan instance.

Walkthrough

The BCLEnhancements application demonstrates how to implement many of these new framework features.

Validating and Converting String Data

You can now easily check the existence of a string value and parse it into another data type by using the new IsNullOrEmpty and TryParse methods. The following code shows this in action:

    Private Sub TryParseButton_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles TryParseButton.Click
        If Not String.IsNullOrEmpty(DateTextBox.Text) Then
            Dim parsedValue As DateTime
            Dim tryResult As Boolean

            tryResult = DateTime.TryParse(DateTextBox.Text, parsedValue)
            If tryResult Then
                MessageBox.Show("The parsed date is " & parsedValue.ToString() & ".")
            Else
                MessageBox.Show("The parse failed.")
            End If
        Else
            MessageBox.Show("Textbox is empty.")
        End If
    End Sub

First, the value from the TextBox is checked by calling String.IsNullOrEmpty. If the TextBox contains a value, you can attempt to parse the value into a DateTime by calling DateTime.TryParse. If either of these operations returns False, a message box is displayed indicating the error.

The SplitButton click event performs splits a string and stores the results as individual items in a ListBox.

    Private Sub SplitButton_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles SplitButton.Click
        Dim colors() As String = ToSplitTextBox.Text.Split( _
            New Char() {","}, _
            StringSplitOptions.RemoveEmptyEntries)

        For Each color As String In colors
            SplitResultsListBox.Items.Add(color)
        Next
    End Sub

You can see that the Split method is passed an argument that indicates that empty entries should be removed.

Aa730864.netvbdev0701(en-US,VS.80).gif

Figure 7-1. Splitting and parsing string.

Generic Collections

The generic List collection lets you literally make a strongly typed list of anything. Declaring a list is simple:

Dim studentList As New List(Of Student)

This code defines a new instance of the List generic that will accept only Student instances. Later, newly created Student objects are added to the collection with the following code:

Dim s As New Student
s.Age = IIf(IsNumeric(AgeTextBox.Text), AgeTextBox.Text, 0)
s.Name = NameTextBox.Text
studentList.Add(s)

Even though this code looks no different than an addition to a normal ArrayList, it is significant because while an ArrayList would allow any object to be added, this collection will allow only the addition of Employee objects.

Monitoring Execution Speed

The BCLEnhancements application also illustrates using the stopwatch class to time an operation. First, a Stopwatch is started using the following code:

Dim  s  As  Stopwatch  =  Stopwatch.StartNew()

Just before the procedure completes, the elapsed time is retrieved and displayed. The elapsed time is give as a TimeSpan data type:

s.Stop()
ElapsedTimeLabel.Text = FormatNumber(s.Elapsed.TotalSeconds(), 3)

This returns a TimeSpan instance, which is displayed in a label in the user interface.

Application Events

The BCLEnhancements application uses a centralized exception handler to handle application errors. Whenever an exception goes unhandled, it is caught by the application UnhandledException event, which uses the Application class to log the error.

Partial Friend Class MyApplication

    Private Sub MyApplication_UnhandledException(ByVal sender As Object, _
    ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) _
    Handles Me.UnhandledException
        MessageBox.Show(e.Exception.Message)
        Application.Log.WriteEntry(e.Exception.ToString())
    End Sub
End Class

The application UnhandledException event is fired any time an application is compiled in release mode (which can be done by pressing CTRL+F5), and an exception goes unhandled. This makes it easy to easily handle errors, log the information, and let the user decide what to do.

Aa730864.netvbdev0702(en-US,VS.80).gif

Figure 7-2. Dealing with unhandled exceptions.

Conclusion

The .NET Framework enhancements presented in this application are not limited to desktop scenarios. Any application—be it a desktop, Web, or service application—can use these enhancements for improved string manipulation, quicker collection generation, more flexible tracing, and easier implementation of resource files.

© Microsoft Corporation. All rights reserved.