Every type in .NET (including types like Integer as well as the EventLog class) is derived from the Object class. This means that every type has Sub New , Equals , GetHashCode , GetType , ReferenceEquals , ToString , Finalize , and MemberwiseClone methods . What isn't obvious are the differences between types like Integer and EventLog .
.NET has a Common Language Specification (CLS). The Visual Basic types you may be familiar with from VB6 don't exactly exist in .NET proper. Yes, there is an Integer type in VB .NET, but it is based in the System.Int32 value type defined in the CLS. Of course, having made it this far, you already know that you can use most of the types you are accustomed to; the compiler will convert the VB types to CLS types.
Regardless, both Integer and the EventLog class have the same root, the Object class. But they behave differently. If simple types like Integer had to be instantiated like EventLog and carried around the baggage of EventLog , .NET would be cumbersome to use. The problem is how to have smart types even for simple things like integers without the overhead of heap allocation and the constructor calling convention. The solution was to branch the .NET framework very early into to main kinds of types: value types and reference types.
Value types are literally types subclassed from the System.ValueType class. Reference types do not follow this inheritance ascendancy. Types derived from ValueType are structures ; types not derived from ValueType are classes . A noticeable difference is that ValueType children are usually not created by invoking the Sub New constructor and fall into simpler categories.
More importantly, value types remain lightweight by not carrying Run Time Type Information (RTTI) ”called just type information in .NET. Reference types do carry type information.
However, both value types and reference types can be queried for type information. When you need type information for a reference type like EventLog , you simply request it by invoking the GetType method. When you need type information for a value type, just like the reference type, you request the type information by invoking GetType .
What is the difference? The difference is that when you request type information on a value type, a process called boxing occurs. We'll return to boxing in a moment. For now, just keep in mind that value types provide the convenience and performance of native types ”which don't really exist in .NET ”with the power and flexibility of classes, all happening quietly behind the scenes.
Structures replace the types used in VB6. When you want something a little simpler than a class, you have the choice of defining a structure.
Structures are more powerful in VB .NET than types were in VB6. Unlike the type construct in VB6, which supported the ability to define only fields, the structure construct in VB .NET supports the ability to define constants, enumerations, fields, properties, methods, and events. For the most part structures are classes that don't have the overhead of type information (unless requested ), do not require (but do support) using the Sub New constructor, and cannot be inherited from.
Subtler differences between structures and classes may be a little harder to detect. They are briefly listed here for reference.
For general purpose use, you declare a structure variable and interact with members of structures using the variable name and dot operator just as you would with a class.
Referring back to the DateTime statement earlier in the chapter, you can declare a DateTime variable and initialize it to the current date and time as follows :
Dim rightNow As DateTime = DateTime.Now
You could also invoke a similar operation on an Integer variable by requesting its string representation.
Dim anInt As Integer = 5 MsgBox(anInt.ToString())
You can also pass data to the parameterized version of a structure's constructor by using the New operator.
Dim d As DateTime = New DateTime(1966, 2, 12) Console.WriteLine(d.ToLongDateString()) Console.ReadLine()
The variable d is instantiated with year 1966, month 2, and day 12. The second statement invokes an operation on the DateTime structure using the variable d .
Structures are significantly more advanced than VB6 types. Most of the time when you are defining new types you will want to define a class.
Structures are associated with the variable they are assigned to. When you assign one instance of a structure to a new variable, you are making a copy of that structure. All of the values of the first structure are copied to the second structure. Each structure occupies a separate space in memory.
When you assign a structure to Nothing , all of the field values in that structure are set to their null equivalent. For example, initializing the DateTime variable d to Nothing would change the date value to Monday, January 1, 0001.
Structure Equality Testing
When you perform equality testing on structures, you will need to compare every field to ensure that the structures contain identical values. Comparing to structure variables ”rather than the fields within ”would always yield inequality.
We will look at the grammar for defining structures in the upcoming Defining Structures and Classes section.
Classes in VB6 were actually more closely related to COM interfaces. VB .NET supports classes and interfaces. Classes in VB .NET support implementation inheritance. Implementation inheritance is completely new in VB .NET (see Chapter 3). For now let's return to our comparison between structures and classes vis- -vis value types and reference types.
Classes are reference types. This means instances of classes are created on the heap using the Sub New constructor. Classes carry their RTTI with them and support all of the things you would expect from a fully object-oriented programming language. (Refer to Chapter 4 for more details on type information.)
VB .NET classes support inheritance, polymorphism, encapsulation, information hiding, and associations. VB .NET classes do not support templates and multiple inheritance. The template and multiple inheritance idioms are supported in C++ but are perceived by many to introduce more problems than they solve. But these beliefs are subject to much conjecture.
To create instances of a class ”referred to as instantiating a class ”you declare a class variable just as you would a structure variable, but you introduce the New operator. The New operator allocates memory to the class variable and invokes the Sub New method, which plays the role of constructor in VB .NET. Here is an example of creating a new instance of an EventLog component.
Dim Log As EventLog = New EventLog()
After you have created an instance of a class, you invoke operations and access members by using the dot-operator syntax. For example, to set the Source property of the Log object you might write the following:
Log.Source = "MySource"
Classes and objects may become confusing when you use a class like an object. Let's take a moment to review the differences between classes and objects.
A class is a description of a type. An object is an instance of a type. For example, one blueprint (class) can be used to create many homes (objects). However, you can also use a class like an object. This happens when you invoke a shared member.
Shared members exist at the class level and are shared across all instances of an object. When we use a class like an object, we refer to that class as a metaclass . For example, there are several overloaded versions of the EventLog.WriteEntry method. (We'll discuss overloading in Chapter 2. For now think of overloading as methods that have the same name, in the same class, but with different arguments. The compiler figures out what method you mean by the arguments you pass to the method.) Regarding the EventLog.WriteEntry method, several overloaded versions can be called without creating an instance of the EventLog class. For example,
writes an entry to the event log using the class EventLog and the shared method WriteEntry . This is just mechanics, but it does seem to cause some confusion among some VB developers. What further exacerbates the confusion is that VB supports variables and classes with identical names . For example, you could declare and create an EventLog object as follows:
Dim EventLog As EventLog = New EventLog()
You may want to avoid defining variables that match class names. Fortunately, the compiler is smart enough to resolve these ambiguities , but the human reader may find the code confusing. Read the Defining Structures and Classes section for examples.
Value Types, Reference Types, and Memory
Value types and reference types behave differently in memory. It is important to have a mental image depicting the differences between value types and reference types. You are less likely to induce memory leaks in VB .NET if you understand these differences. However, you can still trip over instances where two reference types refer to the same object and one reference disposes of the object.
When you declare two value types and assign one to the other, VB .NET performs a copy. Then you have two distinct variables occupying different chunks of memory (Figure 1.1).
Figure 1.1. Two value types ( DateTime structure) after assignment. Each value type structure refers to a separate chunk of memory, and the 'ValueType variables reside at separate locations in memory. Value type assignment performs a deep copy. Modifying one value type has no effect on any other.
When you perform assignment of two reference types you are creating an alias; both names refer to the same chunk of memory. If you modify the properties of one reference type, the change is reflected in the other named reference type (Figure 1.2). If you want separate objects, occupying different chunks of memory, you must allocate memory to each reference type and perform a member-wise copy of each property.
Figure 1.2. Two reference types ( EventLog objects) after assignment. Each object refers to the same chunk of memory. Reference type assignment is analogous to someone having both a given name and a nickname ”both names refer to the same person. In the figure, the names Log1 and Log2 refer to the same object.
If you are familiar with how C++ or Object Pascal deal with pointers, you understand how reference types work in Visual Basic .NET. In C++ vernacular, reference types perform a shallow copy and value types perform a deep copy.
Boxing and Unboxing
While you are coding you need to be familiar with the difference between value types and reference types, especially when you perform assignment. However, you don't need to worry about value types and reference types as they relate to acquiring type information. Reference types carry their type information with them; if you request type information for value types, the compiler will make it available behind the scenes.
When you request type information for a value type, like DateTime , .NET boxes the value type. There is literally a box instruction emitted to IL that creates a reference object, copies the value type to the new object, and returns the type information. When your code is finished with the task requiring a reference type, the IL unbox instruction is executed. Figure 1.3 shows some IL code in the ildasm.exe utility (see the Intermediate Language section at the end of this chapter for an explanation) that boxes an Integer structure when the type information is requested.
Module Module1 Sub Main() Dim I As Integer = 5 Console.WriteLine(I.GetType().FullName) End Sub End Module
Figure 1.3. The box instruction at IL_0004 , which copies the Integer structure to a reference type to get the type information.
The preceding code fragment requests the type information, which implicitly instructs the compiler to box the Integer structure. The full name of the Visual Basic .NET Integer type is System.Int32 . The Common Type Specification does not define an Integer type. Integer was maintained to promote familiarity with VB6.
You may also have noticed from the IL listing shown in Figure 1.3 that the unbox instruction is not called. While you need to know that types like Integer are really objects derived from the Object class, and you need to know that the ValueType classes make simple types behave like native types, you don't need to worry about when the compiler boxes and unboxes. The compiler has the responsibility of box and unboxing. All you and I need to know is that value types behave like native types but actually contain data and methods.