Chapter 4. Data Types and Expressions

Every programming task manipulates data. Data can be of different kinds; you will often work with strings, dates and time, numbers, files, and custom data. Each of them is represented by a data type. The .NET Framework 4.5 (but also the Windows Runtime for Windows 8) provides tons of built-in data types and enables developers to easily create their own custom data types. In this chapter you learn how the .NET Framework handles data types and how you can work with value types and reference types. When you gain knowledge of data types, you can learn how to use them with special Visual Basic language constraints such as loops, iterations, and special statements. This is a fundamental chapter, so you should pay particular attention to the concepts that you need to understand before we discuss the object-oriented programming with Visual Basic 2012.

Introducing the Common Type System

The .NET Framework provides a special way for manipulating data types, which is named Common Type System. The Common Type System is important, so you need to know something about it before reading discussions on data types. In its name, the word Common has two particular meanings. First, the Common Type System provides a unified model for exposing data types so that all the .NET languages, such as Visual Basic, Visual C#, and Visual F#, can consume the same data types. For example, a 32-bit integer is represented by the System.Int32 data type, and all the .NET languages can invoke the System.Int32 object for declaring integers because this type is provided by the .NET Framework and is language-independent. Second, each data type is an object that inherits from the System.Object class as we discuss next.

Everything Is an Object

In the .NET development you might hear that everything is an object. This is because all the types in the .NET Framework, including built-in and custom types, inherit from the System.Object class. Inheritance is a concept that is part of the object-oriented programming topic and that is discussed later in the book. We can define it as a way for reusing and extending data types so developers can create their hierarchy of types. System.Object provides the primary infrastructure all .NET types must have. The .NET Framework ships with thousands of built-in data types that all derive from System.Object. But why is this class so important in the Common Type System? The answer is simple: the Common Type System ensures that all .NET types inherit from System.Object; particularly both value types and reference types inherit from System.Object. At this point an overview of value types and reference types is required before delving into both categories.

Introducing Value Types and Reference Types

Value types are those data types that store their data directly. Examples of value types are integers (System.Int32), Boolean (System.Boolean), and bytes (System.Byte). Value types are stored in a memory area called Stack. They are represented by (and defined via) structures that are enclosed in Structure..End Structure code blocks. The following is an example of a value type containing a value:

Dim anInteger As System.Int32 = 5

Reference types are instead data types that, as their name implies, are just a reference to the actual data. In other words, reference types store the address of their data in the Stack whereas the actual data is stored in the Managed Heap. Reference types are represented by classes. The following is an example of reference type:

Class Person
    Property FirstName As String
    Property LastName As String
End Class


Don’t Be Afraid of Memory Locations

This paragraph is just an overview of value types and reference types. If you never heard about the Stack and the Managed Heap, don’t worry. Details will be provided later in this chapter, when discussing value and reference types.


Reference types inherit directly from System.Object or from other classes that derive from Object. This is because System.Object is a reference type. So the question that you will probably ask now is “If both value types and reference types have to inherit from System.Object, how can value types inherit from System.Object if it is a reference type?” The answer is that in the case of value types an intermediate type named System.ValueType inherits from System.Object and ensures that all deriving objects are treated as value types. This is possible because the Common Language Runtime can distinguish how types are defined and consequently can distinguish between value types and reference types.


Naming System.Object

Object is also a reserved word of the Visual Basic programming language and is the representation of System.Object. Because of this, we refer indistinctly to Object as System.Object.


System.Object and System.ValueType

At this point, it is necessary to provide an overview of both System.Object and System.ValueType. Instead of showing a diagram of inheritance, it is a good idea to offer a Visual Studio–oriented view so you can better understand what happens within the development environment. This can be accomplished via the Object Browser tool window introduced in Chapter 2, “Getting Started with the Visual Studio 2012 IDE.” You can browse for both classes by just writing their names in the search box. Figure 4.1 shows how System.Object is defined, which members it exposes, and a full description.

Image

Figure 4.1. System.Object shown in detail in the Object Browser tool window.

Several things are worth mentioning. First, consider the class description. As you can see, System.Object is defined as the root of the type hierarchy, and all other classes within the .NET Framework derive from System.Object. This means that custom classes automatically inherit from Object. This class provides the infrastructure for all derived classes and exposes some methods. Because System.Object is important and because you will often invoke methods inherited from Object, it’s convenient to get a simple reference for each method. Table 4.1 describes methods exposed by System.Object.

Table 4.1. Methods Exposed by System.Object

Image

Methods listed in Table 4.1 are covered several times during the rest of the book, so don’t be afraid if something is not clear at the moment. If you now refer to Figure 4.1, you can see how the Object class has no base types. This is because, as we said before, Object is the root in the type hierarchy. Considering this last sentence, if you instead try to expand the Derived types node, you see a list of hundreds of .NET Framework built-in classes that derive from Object. One of these classes is System.ValueType. Figure 4.2 shows how this class is represented in the Object Browser.

Image

Figure 4.2. System.ValueType shown in detail in the Object Browser.

You should focus on the description first. As you can see, System.ValueType is the base class for all value types. It is declared as MustInherit (a clause discussed in detail in Chapter 12, “Inheritance”) that means it must necessarily be inherited and that it can’t work as a standalone object, providing the base infrastructure for derived value types. If you expand the Base types node, you see that ValueType is a derived class from Object. If you then try to expand the Derived types node, you get a large list of types that inherit from ValueType, such as Boolean, Byte, and other primitive data types. As mentioned before, the CLR can determine that a type deriving from System.ValueType must be treated as a value type and not as a reference type.

Understanding Value Types

Value types are data types that directly store data that they define. For example, a System.Int32 object represents a value type that can store an integer number as in the following line of code:

Dim anInteger As System.Int32 = 5

Among value types, the most common are numeric types that enable, for example, performing math operations or implementing counters or just storing a numeric value. Value types enable developers to choose the best data type according to the particular scenario. As we mentioned at the beginning of this chapter, value types are Structure objects. The .NET Framework provides several built-in value types that cover most needs in your development process, although you can create custom data types. In this section you learn about most common built-in value types and about building your own structures, including how value types are declared and used and how they are stored in memory.

.NET Framework Primitive Value Types

The .NET Framework Base Class Library provides lots of built-in value types that you can use according to your needs. Each value type is a structure exposed by the Base Class Library.

Visual Basic 2012 provides reserved words that are counterparts of the most common value type names. For example, the System.Int32 value type has an alias in the Integer reserved word. The following two lines of code are perfectly equivalent:

Dim anInteger As System.Int32 = 0
Dim anInteger As Integer = 0

You might use indistinctly both .NET names and the Visual Basic reserved words when referring to built-in value types.


Naming Conventions

Although you are allowed to invoke value types with both .NET names and the Visual Basic counterparts’ keywords, it’s a best practice to choose the .NET names when developing reusable class libraries according to the Microsoft Common Language Specification. We discuss this topic later in this chapter. In such a scenario it is a good practice because you should avoid language-dependent features and practices when developing assemblies bound to also work with other .NET languages; this will not change the results, but your code will be cleaner, and you will use a .NET-oriented approach instead of a language-oriented one.


Table 4.2 lists the most common value types in the .NET Framework, showing a description and the Visual Basic-related keywords.

Table 4.2. Most Common Value Types in the .NET Framework 4.5

Image
Image

Upgrading from Visual Basic 6

If you are upgrading from Visual Basic 6, you have to keep in mind that VB 6’s Long is an Integer in .NET and that VB 6’s Integer is Short in .NET.


As you might notice in Table 4.2, most built-in value types are exposed by the System namespace except for the BigInteger type that is instead exposed by the System.Numerics namespace.


Memory Requirements

You might wonder what should influence your choice when working with value types. The answer is that it depends. Of course, you should take care of memory allocation. If you know that you need to work with a small number, you will probably do best if choosing a Byte instead of a Short. Regarding this, consider that Byte requires 8 bits, Short requires 16 bits, Integer and Single require 32 bits, Long and Double require 64 bits, and Decimal requires 128 bits. The Visual Basic compiler is optimized for 32-bit integers, so choosing Integer is of course a better choice than Short, but, in the case of very small numbers, choose Byte (1 byte) instead of Integer (4 bytes).


Using Value Types

In this paragraph you learn to use value types. The following demonstration assumes you have created a new Visual Basic project for the Console (see Chapter 2 for details). Listing 4.1 shows how you can declare variables storing value types. You can write the code inside the Main method for learning purposes.

Listing 4.1. Using Value Types


Sub Main()
    'Declares an Integer
    Dim anInteger As Integer = 2

    'Declares a double and stores the result of a calculation
    Dim calculation As Double = 74.6 * 834.1

    'Declares one byte storing a hexadecimal value
    Dim oneByte As Byte = &H0

    'Declares a single character
    Dim oneCharacter As Char = "a"c

    'Declares a decimal number
    Dim sampleDecimal As Decimal = 8743341.353531135D

    'Declares a Boolean variable
    Dim isTrueOrFalse As Boolean = True

    'Declares a BigInteger
    Dim arbitraryInteger As New System.Numerics.BigInteger(800000)

    Console.WriteLine(anInteger)
    Console.WriteLine(calculation)
    Console.WriteLine(oneByte)
    Console.WriteLine(oneCharacter)
    Console.WriteLine(isTrueOrFalse)
    Console.WriteLine(arbitraryInteger)
    Console.ReadLine()
End Sub


You can declare variables of the desired value types using the Dim keyword followed by the identifier of the variable and by the As clause that then requires the type specification.


Notes About Dim and As

Dim is the most important keyword for declaring variables and is commonly used in local code blocks. It is also worth mentioning that in .NET Framework you can declare different kinds of objects both with Dim and with other keywords according to the scope of the objects (such as fields, properties, and classes). This is discussed in Chapter 7, “Class Fundamentals.” Then in Chapter 20, “Advanced Language Features,” you learn about another important feature in .NET Framework known as the Local Type Inference that avoids the need to add the As clause in particular scenarios such as data access with LINQ.


You can also declare more than one variable within the same Dim statement. You can write something like this:

Dim anInteger As Integer = 2, calculation As Double = 3.14,
    TrueOrFalse As Boolean = True

You can also declare more than one variable of the same type just by specifying such a type once, as in the following line of code:

'Three integers
Dim anInteger, secondInteger, thirdInteger As Integer

If you upgrade from Visual Basic 6, this is a great change because in a declaration like the preceding one VB 6 automatically assigns Variant instead of the appropriate data type. Generally you do not need to specify the constructor (the New keyword) when declaring value types. This is because in such situations the constructor addition is implicit and provided by the compiler behind the scenes. An exception to this general rule is the BigInteger type that instead allows the constructor to be explicit, but it also allows inline initialization. Listing 4.1 also shows how you can get the value stored in value types. In our example values are written to the Console window, but you can use values the most appropriate way for you. Figure 4.3 shows the result of the code in Listing 4.1.

Image

Figure 4.3. Using value types in our sample code produced this result.

Pay attention when using Char and String data types. Because both types require their content to be enclosed within quotes, the value of a Char must be followed by the C letter that tells the compiler to treat that value as a single character (if you are using Option Strict On). The Decimal data type also has a similar behavior. When you declare a decimal value (see Listing 4.1), you must ensure that the value is followed by the upper D character; otherwise, the compiler treats the number as a Double and raises an error. Identifiers like C and D are also known as literal type characters and are available for a number of primitive types, as summarized in Table 4.3.

Table 4.3. Literal Type Characters

Image

Literal type characters are not available for the following types:

Boolean

Byte

Date

Object

SByte

String

Assigning Value Types

At the beginning of the section, we mentioned that value types directly store the data to which they refer. This can be easily verified with assignments. Consider the following code:

Sub DoAssignments()

    Dim anInteger As Integer = 10
    Dim anotherInteger As Integer = anInteger

    Console.WriteLine(anInteger)
    Console.WriteLine(anotherInteger)

    Console.ReadLine()

End Sub

This code produces the following output:

10
10

This is because the value of anInteger has been assigned to another variable of type Integer, named anotherInteger. anotherInteger is a copy of the first variable and lives its own life, independent from anInteger. If you now write the following line of code after anotherInteger assignment

anotherInteger = 5

the code produces the following output:

10
5

So you have changed the value of anotherInteger while you left unchanged the value of anInteger because they are two different objects with separate lives. Although this can appear obvious, it is important because it is the base for later understanding the different behavior in reference types. You might also use assignments in situations in which you need to get a result without knowing values that produce the result itself, such as in calculations that require an input from the user. With regard to this, consider the following code:

Dim firstNumber As Double = 567.43
Dim secondNumber As Double = 321.52

Dim result As Double = firstNumber * secondNumber

Console.WriteLine(result)
Console.ReadLine()

In the preceding code, you get the result of a multiplication given two numbers. In real scenarios, the two numbers would be provided by the user and the result variable would store the result of the calculation. Such calculations are performed not on numbers but on the value of variables that store numbers. This means that you do not need to know in advance the numbers; you just work on variables and assignments.


Note

You could probably expect a code example in which the input should be provided by the user. Reference types, conversion operators, and parsing methods have not been discussed yet—those will be discussed later. So here the goal was to avoid confusion because user input is provided as String objects. There will be appropriate code examples when needed, as in the next paragraph.


Analyzing the Content of Value Types

In most cases you will require users to enter an input that you will then need to elaborate, or you could simply read the content of a text file and then convert such content into the appropriate .NET value type. Typically, user input is provided as strings; you will also get strings when reading text files. In business applications you need to implement validation rules on the user input, but it is worth mentioning that value types offer common methods for analyzing the contents of a string and check whether such content matches a particular value type. Even if we have not discussed the String object (which is a reference type), yet the following code samples are easy to understand. For example, consider the following code that declares some strings:

Dim firstValue As String = "1000"
Dim secondValue As String = "True"
Dim thirdValue As String = "123.456"

The content of each string is a representation of a particular value type: firstValue content represents an integer, secondValue represents a Boolean, and thirdValue represents a Double. We could parse the content of each string and transform it into the appropriate value type as follows:

Dim anInteger As Integer = Integer.Parse(firstValue)
Dim aBoolean As Boolean = Boolean.Parse(secondValue)
Dim aDouble As Double = Double.Parse(thirdValue)

As you can see, value types expose a method called Parse that converts the string representation of a numeric or logical value into the correspondent value type. Integer.Parse converts the “1000” string into a 1000 integer and so on. If the compiler cannot perform the conversion, a FormatException error will be thrown and the application execution will be broken. To avoid possible errors, you could use another method called TryParse that returns True if the conversion succeeds or False if it fails. For example, consider the following code:

Dim testInteger As Integer
Dim result = Integer.TryParse(secondValue, testInteger)

The code attempts to convert a string that contains the representation of a Boolean value into an Integer. Because this is not possible, TryParse returns False. Notice that in this particular case you don’t perform an assignment to another variable (such as in the case of Parse) because TryParse requires the variable that will store the value as the second argument, passed by reference (a more detailed explanation on passing arguments by reference is provided in Chapter 7).


Value Types Methods

Because the purpose of this book is to examine the Visual Basic 2012 language features—and examining all the .NET Framework available types and members isn’t possible—only methods common to all value types are described. Methods and members specific to some value types are left to you for future studies. Always remember that when you do not know an object member, IntelliSense and the Object Browser together with the MSDN documentation can be your best friends, providing useful information.


Value types (including System.Char and System.DateTime) also expose two properties named MinValue and MaxValue that contain the minimum accepted value and the maximum accepted value for each type, respectively. For example, the following line of code

Console.WriteLine(Integer.MinValue)

produces -2147483648 as the result. The following line of code

Console.WriteLine(Char.MaxValue)

produces the ? character as the result. Finally, the following line of code

Console.WriteLine(Date.MaxValue)

produces 12/31/9999 11:59:59PM as the result.

MinValue and MaxValue can be useful for two purposes: The first purpose is for times when you don’t remember the minimum and maximum values accepted by a particular value type. The second purpose deals with comparisons—you might need to check whether a value or a number is in the range of accepted values by a particular data type. Now we have completed a general overview of value types. Next, we focus on optimizations and on using special value types such as System.DateTime.

Optimization Considerations

When working with value types, you should always choose the best type for your needs. For example, if you need to work with a number composed in the Integer range, you should not use a Long type. Moreover, the Visual Basic compiler (and, behind the scenes, the CLR) provides optimizations for the System.Int32 and System.Double types, so you should always use these types when possible. For example, use an Int32 instead of an Int16, although the number you work with is composed in the range of the Int16. Other considerations about unsigned value types are related to compliance with Microsoft Common Language Specification, which is a topic discussed later in this chapter.


Nullable Types

Sometimes you need to assign null values to value types—for example, when mapping SQL Server data types to .NET data types for fetching data. To accomplish this, the .NET Framework provides support for the nullable types. Because nullable types have the syntax of Generics objects, and because you first need to know how Generics work, they are discussed in Chapter 14, “Generics and Nullable Types.”


Working with BigInteger

The System.Numerics.BigInteger is a value type exposed by the System.Numerics namespace and requires a reference to the System.Numerics.dll assembly. It represents a signed, arbitrarily large integer number. This means that it doesn’t have minimum and maximum values, opposite of other value types such as Integer and Long. Instantiating a BigInteger is easy, as you can see from the following line of code:

Dim sampleBigInteger As New System.Numerics.BigInteger

You can assign any signed number to a BigInteger because it has no minimum and maximum values, as demonstrated by the following code snippet:

'Neither minimum nor maximum values
sampleBigInteger = Byte.MinValue
sampleBigInteger = Long.MaxValue

Byte and Long are the smallest and the biggest acceptable signed integers, respectively. BigInteger directly supports integer types such as SByte, Byte, UInteger, Integer, UShort, Short, ULong, and Long. You can also assign to a BigInteger the values of type Double, Single, and Decimal, but you do need to accomplish this passing the value as an argument to the constructor or performing an explicit conversion using CType (assuming that Option Strict is On). The following code demonstrates both situations:

'The constructor can receive arguments, Double is accepted
Dim sampleBigInteger2 As New  _
                      System.Numerics.BigInteger(123456.789)

'Single is accepted but with explicit conversion
Dim singleValue As Single = CSng(1234.56)
Dim sampleBigInteger3 As New System.Numerics.BigInteger
sampleBigInteger3 = CType(singleValue,
                    Numerics.BigInteger)

Notice that rounding will occur when converting floating types to BigInteger. Such structure also offers shared methods for performing arithmetic operations. You can add, subtract, divide, and multiply BigIntegers as in the following code:

'Assumes an Imports System.Numerics directive
'Sum
Dim sum As BigInteger =
    BigInteger.Add(sampleBigInteger, sampleBigInteger2)

'Subtract
Dim subtraction As BigInteger =
    BigInteger.Subtract(sampleBigInteger, sampleBigInteger2)

'Division
Dim division As BigInteger =
    BigInteger.Divide(sampleBigInteger, sampleBigInteger3)

'Multiplication
Dim multiplication As BigInteger =
    BigInteger.Multiply(sampleBigInteger2, sampleBigInteger3)

You can also perform complex operations, such as exponentiation and logarithm calculations as demonstrated here:

'Power
Dim powerBI As BigInteger = BigInteger.Pow(sampleBigInteger2, 2)

'10 base logarithm
Dim log10 As Double = BigInteger.Log10(sampleBigInteger3)

'natural base logarithm
Dim natLog As Double = BigInteger.Log(sampleBigInteger, 2)

As usual, IntelliSense can be your best friend when exploring methods from BigInteger and can help you understand what other math calculations you can perform.

Building Custom Value Types

Building custom value types is accomplished by creating structures. Because creating structures can also be a complex task and is part of the object-oriented programming topic, it is thoroughly discussed in Chapter 11, “Structures and Enumerations.”

Understanding Reference Types

Reference types are represented by classes. Classes are probably the most important items in modern programming languages and are the basis of object-oriented programming. Reference types have one big difference versus value types. Variables that declare a reference type do not store the data of the type itself; they just store an address to the data. In other words, they are just pointers to the data. To better explain (and understand) this fundamental concept, let’s look at an example. Consider the following class Person, which exposes two simple properties:

Class Person

    Property FirstName As String
    Property LastName As String

End Class

You need to create an instance of such a class so that you can store data (in this case, setting properties) and then manipulate the same data. This can be accomplished by the following lines of code:

Dim onePerson As New Person
onePerson.FirstName = "Alessandro"
onePerson.LastName = "Del Sole"


Strongly Typed Objects

In .NET development, you often encounter the words strongly typed. This definition can be explained with an example. The onePerson object in the preceding code is strongly typed because it is of a certain type, Person. This means that onePerson can accept an assignment only from compliant objects, such as other Person objects. Such restriction is important because it avoids errors and problems. Moreover, the compiler knows how to treat such a specialized object. A variable of type Object is instead not strongly typed because it is just of the root type and is not specialized. Object can accept anything, but without restrictions the usage of non-strongly typed objects could lead to significant problems. Chapter 14 discusses Generics, so you’ll get a more thorough understanding of strongly typed objects.


Now you have an instance of the Person class, named onePerson. Consider the following line of code:

Dim secondPerson As Person = onePerson

A new object of type Person (secondPerson) is declared and is assigned with the onePerson object. Because of the equality operator, you would probably expect secondPerson to be an exact copy of onePerson. We could consider at this point some edits to the secondPerson object; for example, we could modify the first name:

secondPerson.FirstName = "Alex"

We can try to check the result of the previous operations by simply writing the output to the Console window. Let’s begin by writing the result of secondPerson:

Console.WriteLine(secondPerson.FirstName)
Console.WriteLine(secondPerson.LastName)
Console.ReadLine()

As you might correctly expect, the preceding code produces the following result:

Alex
Del Sole

Now let’s simply write the result for onePerson to the Console window:

Console.WriteLine(onePerson.FirstName)
Console.WriteLine(onePerson.LastName)
Console.ReadLine()

This code produces the following result:

Alex
Del Sole

As you can see, editing the first name in secondPerson also affected onePerson. This means that secondPerson is not a copy of onePerson. It is instead a copy of the reference to the actual data. Now you should have a better understanding of reference types. We can say that, as their name implies, reference types have an address in memory where data is stored and variables declaring and instantiating reference types just hold a reference to that data. To get a real copy of data, you should write something like this:

Dim secondPerson As New Person
secondPerson.FirstName = onePerson.FirstName
secondPerson.LastName = onePerson.LastName

Then you could edit secondPerson’s properties, ensuring that this will not affect the onePerson object. This kind of technique for creating a clone for a reference type is good with objects exposing only a few properties, but fortunately there are more interesting techniques to clone more complex objects. These are discussed in the “Deep Copy and Shallow Copy” section. As a clarification, notice that, in the .NET Framework, String is a reference type but it’s actually treated as a value type, as I will explain in a few paragraphs.

.NET Framework Primitive Reference Types

The .NET Framework 4.5 ships with tons of reference types exposed by the Base Class Library (BCL) and that cover most needs. However, you will often use several reference types in the development process that many other reference types derive from. Table 4.4 lists the most common reference types.

Table 4.4. Most Common Built-In Reference Types in .NET Framework

Image

You can use a number of reference types when developing real-life applications, and most of them are discussed in subsequent chapters; however, the ones listed in Table 4.4 provide the basis for working with reference types. Most of them are the base infrastructure for other important derived classes. We previously discussed System.Object, so we will not do it again. It is instead worth mentioning how System.String is a reference type, although it seems natural to think about it as a value type. System.String, or simply String, is used as a value type, so it will not be difficult to build strings. By the way, strings are immutable (that means “read-only”), so each time you edit a string, the runtime creates a new instance of the String class and passes in the edited string. Because of this, editing strings using System.String can cause unnecessary usage of memory. To solve this problem, the .NET Framework provides more efficient ways, as you will see in the sections “Working with Strings,” “Working with Dates,” and “Working with Arrays.”

Differences Between Value Types and Reference Types

Value types and reference types differ in several ways. In the previous sections we saw how they differ in assignment and how a value type can directly store data, whereas a reference type stores only the address to the actual data. We now explain such implementation and provide information on the other differences between value and reference types.

Memory Allocation

Value types and reference types are differently allocated in memory. Value types are allocated in the Stack. The Stack is a memory area where methods are executed according to the last-in, first-out manner. The first method pushed to the Stack is the application entry point; that is, the Main method. When Main invokes other methods, the CLR creates a sort of restore point and pushes those methods to the Stack. When the method needs to be executed, data required by that method is also pushed to the Stack. When a method completes, the CLR removes (popping) it from the Stack together with its data, restoring the previous state. Because of this ordered behavior, the Stack is efficient, and the Common Language Runtime can easily handle it. Consider the following line of code, declaring a variable of type Integer, therefore a value type:

Dim anInteger As Integer = 5

Figure 4.4 shows how the anInteger variable is allocated in the Stack.

Image

Figure 4.4. Value types are allocated in the Stack.

On the contrary, reference types are allocated in a memory area named Managed Heap. Different from how objects are treated in the Stack, objects in the Managed Heap are allocated and deallocated randomly. This provides fast allocation but requires more work for the CLR. To keep things ordered, the CLR needs two instruments: the Garbage Collector and Memory Manager. We provide details about this architecture in Chapter 8, “Managing an Object’s Lifetime.” At the moment you need to understand how reference types and their data are allocated. Consider the following lines of code, declaring a new version of the Person class and an instance of this class:

Class Person

    Property Name As String
    Property Age As Integer
End Class
Dim onePerson As New Person
onePerson.Name = "Alessandro Del Sole"
onePerson.Age = 35

As you can see, there is now a property in this class (Age) that is a value type. The instance of the class will be allocated in the Heap, and its reference (onePerson) will be allocated in the Stack. Figure 4.5 provides a visual representation of this scenario.

Image

Figure 4.5. Reference types are allocated in the Heap, whereas their addresses reside in the Stack.

Because the Person class handles a value type in one of its properties, such value types stay in the Stack. Figure 4.6 completes this overview. In the second part of this book, you will have different opportunities to explore reference types and memory management when discussing the object’s lifetime.

Image

Figure 4.6. Complete overview of memory allocation for value and reference types.

Object-Oriented Differences

There are a couple of differences with regard to principles related to object-oriented programming. Although these principles are discussed in detail in Part II, “Object-Oriented Programming with Visual Basic 2012,” it’s convenient to mention them here.

Inheritance

The first principle deals with inheritance, which is covered in Chapter 12. Classes (that is, reference types) support inheritance, whereas structures (value types) do not. Consider the following code:

Class Person

    Property FirstName As String
    Property LastName As String

End Class

Class Developer
    Inherits Person

    Property UsedProgrammingLanguage As String

    Public Overrides Function ToString() As String
        Return Me.LastName
    End Function
End Class

In this example, the Person class is the base class and provides the basic properties for representing a hypothetical person. The Developer class inherits from Person (see the Inherits keyword), and this means that the Developer class will expose both the FirstName and LastName properties plus the new one named UsedProgrammingLanguage. It also redefines the behavior of the default ToString method so that it can return a more significant name for the object. In Visual Basic 2012, you can inherit from only one object at a time. Thus, Developer can inherit only from Person and not from any other object. If you need multiple-level inheritance, you should architect your object’s framework so that a second class can inherit from the first one, the third one from the second one, and so on. Structures do not support inheritance at all, except for the fact that they inherit by nature from System.ValueType.

Interfaces Implementation

Both classes and structures provide support for interfaces implementation. For example, you could implement the IComparable interface in both cases:

Class Person
    Implements IComparable

    Property FirstName As String
    Property LastName As String

    Public Function CompareTo(ByVal obj As Object) As Integer Implements
           System.IComparable.CompareTo
        'Write your code here
    End Function
End Class

Structure Dimension
    Implements IComparable

    Public Function CompareTo(ByVal obj As Object) As Integer Implements
           System.IComparable.CompareTo
        'Write your code here
    End Function

    Property X As Integer
    Property Y As Integer
    Property Z As Integer
End Structure

Inheritance and interfaces are discussed in Part II, so don’t worry if something is not clear.

Constructors

When you declare a reference type, you need an instance before you can use it (with the exception of shared classes, which are discussed in Chapter 7). You create an instance by invoking the constructor via the New keyword. When you declare a value type, the new variable is automatically initialized to a default value; this is usually zero for numbers and False for Boolean. Because of this, value types do not need to have a default constructor invoked. The Visual Basic compiler still accepts declaring a value type and invoking the constructor, which also initializes the type with the default value. However, in this case you cannot initialize the value. The following code snippet demonstrates this:

'Declares an Integer and sets the value to zero
Dim anInt As New Integer
'Initialization not allowed with New
Dim anotherInt As New Integer = 1
'Allowed
Dim aThirdInt As Integer = 1

Finalizers

Finalizers are related to the object’s lifetime, as discussed in Chapter 8. As for constructors, it’s convenient having a small reference. We previously said that when methods using value types complete their execution, they are automatically removed from the Stack together with the data. This is managed by the CLR, and because of this ordered behavior, value types do not need to be finalized. In contrast, reference types are allocated on the Heap and have a different behavior. Deallocation from memory is handled by the Garbage Collector that needs finalizers on the reference type’s side to complete its work.

Performance Differences

A lot of times value types store data directly, whereas reference types store only the address of the data. Although you can create and consume types according to your needs, there are some concerns with performance, particularly regarding methods. Methods can accept parameters, also known as arguments. Arguments can be value types or reference types. If you pass a value type to a method, you pass to that method all the data contained in the value type, which could be time-consuming and cause performance overhead. Passing a reference type passes only the address to the data, so it could be faster and more efficient. There could be situations in which you need to pass one or more value types to methods. This depends on your needs. Generally, the performance difference in such a scenario is not relevant, but it will depend on the size of the value type. If your method receives a large value type as an argument but is invoked only once, performance should not be affected. But if your method is invoked many times, perhaps passing a reference type would be better. Just be aware of this when implementing your methods.

What Custom Type Should I Choose?

Answering this question is not simple. It depends. If you need to implement a custom type that will act similarly to a value type (for example, a type that works with numbers), you should choose a Structure. If you need to implement an object for storing a large amount of data, it could be a good idea to choose a class. Such considerations are not mandatory, and their purpose is simply to let you think a little bit more about what you are going to implement and what your needs are.

Converting Between Value Types and Reference Types

In your developer life, you often need to convert one data type into another in different types of situations. For example, you might need to convert a value type into another one or just convert an instance of type Object into a strongly typed object. In this section, you learn how to convert between data types and about conversion operators, beginning with basic but important concepts, such as implicit conversions, boxing, and unboxing.

Understanding Implicit Conversions

Previously we discussed the System.Object class. As you might remember, such a class is the root in the class hierarchy. That said, you can assign both reference types and value types to an Object instance because they both inherit from System.Object. Consider the following lines of code:

Dim anInt As Object = 10
Dim onePerson As Object = New Person

The first line assigns a value type (Integer) to an Object, and the second one assigns an instance of the Person class to an Object. Visual Basic 2012 always enables such assignments because they are always safe. What is unsafe is trying to assign an Object to a strongly typed instance, such as assigning Object to an instance of the Person class. This is quite obvious because Object can represent whatever type, and the compiler cannot be sure if that type is a Person, which can cause errors. Consider the following line of code:

Dim onePerson As Person = New Object

The code is trying to assign an instance of the Object class to an instance of Person. The Visual Basic compiler enables handling such situations in two different ways, depending on how Option Strict is set. We discussed Option Strict in Chapter 2, and now you can see the first usage. If Option Strict is set to On, the preceding line of code causes an error. The Visual Basic compiler does allow an implicit conversion from Object to a strongly typed object, throwing an error message that you can see in the code editor. Figure 4.7 shows this error message.

Image

Figure 4.7. With Option Strict On, the Visual Basic compiler disallows implicit conversions.

This is useful because it prevents type conversion errors. If you want to perform an assignment of this kind, you need to explicitly convert Object into the appropriate data type. For example, this can be accomplished using the CType conversion operator, as in the following line of code:

Dim onePerson As Person = CType(New Object, Person)

A conversion operator offers another advantage: It communicates whether the conversion is possible; if it’s not, you can handle the situation, particularly at runtime. The code editor also simplifies the addition of a CType conversion by offering a convenient correction pop-up you enable by clicking the small red line that appears under the bad object and that will automatically convert into the appropriate type (see Figure 4.8 for an example). It is worth mentioning that CType will work without throwing an exception only if the target type for the conversion is the correct type.

Image

Figure 4.8. Using the error correction options to easily add CType.

By the way, the Visual Basic compiler provides a way to allow implicit conversions avoiding error messages. To accomplish this, you need to set Option Strict to Off. You can write the following line of code (preceding all the other code and Imports directives):

Option Strict Off

You could also adjust Option Strict settings in the Compiler tab of the My Project window, as shown in Chapter 2. Assigning an Object to a Person, as we did before, is perfectly legal. But please be careful: If you do not need to perform such assignments, please avoid Option Strict Off and instead use Option Strict On. This can ensure less runtime and compile time errors and enable you to write more efficient code.


Option Strict Off: When?

You should almost never set Option Strict to Off. There is only one situation in which you should set Option Strict to Off—late binding. This topic is discussed in Chapter 46, “Reflection.” Outside this particular scenario, never set Option Strict to Off so that you can be sure that you are working with strongly typed objects and that the compiler, debugger, and CLR will enable you to quickly find errors. The other reason you should not set Option Strict to Off is because performance will suffer. You’ll learn about this, and other reasons, in Chapter 14. I suggest you set Option Strict to On as a default in the Visual Studio 2012 options.


Boxing and Unboxing

The Common Type System enables implicit conversions. It also enables conversions between reference types and value types, and vice versa, because both inherit from System.Object. You often need to work with two techniques, boxing and unboxing, when you have methods that receive arguments of type Object. See the tip at the end of this section for details.

Boxing

Boxing occurs when converting a value type to a reference type. In other words, boxing is when you assign a value type to an Object. The following lines of code demonstrate boxing:

Dim calculation As Double = 14.4 + 32.12
Dim result As Object = calculation

The calculation variable, which stores a value deriving from the sum of two numbers, is a Double value type. The result variable, which is of type Object and therefore a reference type, is allocated in the Heap and boxes the original value of calculation so that you now have two copies of the value, one in the Stack and one in the Heap. Figure 4.9 shows how boxing causes memory allocation.

Image

Figure 4.9. Boxing causes both Stack and Heap allocation.

Boxing requires performance overhead. This will be clearer when reading the next section.

Unboxing

Unboxing occurs when you convert a reference type to a value type. You perform unboxing when converting an Object into a value type. Continuing with the previous example, the following line of code demonstrates unboxing:

Dim convertedResult As Double = CType(result, Double)

Unboxing can cause another copy of the original value (the same stored in the calculation variable) to be created and allocated in the Stack. Figure 4.10 shows a representation of what happens when unboxing a value.

Image

Figure 4.10. Unboxing causes a third copy of the original value to be created.

Boxing and unboxing also cause performance overhead, so they should always be avoided if not truly needed. This is because value types directly store the data they refer to and therefore create three copies of the data, which can consume more resources than necessary. If the value types you box and unbox are small, performance might not be influenced (or, better, you might not see the difference). But if the value types store a large amount of data, the loss of performance could be significant.


Avoiding Boxing and Unboxing

Boxing and unboxing can be necessary if you have methods that receive arguments of type Object. There are a couple of best practices that you can take when implementing methods, such as using Generics or implementing overloads of methods that can accept multiple strongly typed arguments. Generics and overloads are discussed later in the book.



Early Binding and Late Binding

When talking about reference types, another important topic is early binding and late binding. Although this might be the right place to discuss them, I prefer to postpone such discussion until Chapter 46. This way, you first get a complete overview of reference types and then get real examples of the late binding technique.


Deep Copy and Shallow Copy

In the “Understanding Reference Types” section, you saw how reference type assignments differ from value type assignments and how assignments are not enough to create a copy of a reference type. We also provided one basic solution to this problem, which was to create a new instance of a specified reference type and then assign each property of the target instance with values coming from the original one. But this is not enough, both because it is not complete and because it can be good only with small classes. To create a complete clone of a reference type, in the .NET development we can take advantage of two techniques: deep copy and shallow copy. Both techniques require the implementation of the ICloneable interface. Although we discuss interfaces later, the concepts presented here are easy to understand. The ICloneable interface provides a unique method named Clone that enables developers to know that classes exposing such a method can easily be cloned. For example, consider the following implementation of the Person class that also implements the ICloneable interface:

Class Person
    Implements ICloneable

    Property FirstName As String
    Property LastName As String
    Property Work As Job

    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone
    End Function
End Class

The most interesting thing in the code is the Clone method required by the ICloneable interface. In the method body, you should write code that performs the real copy of the reference type. Fortunately, the Object class provides a method named MemberwiseClone that automatically returns a shallow copy of your reference type. The keyword Me indicates the current instance of the class. Because Clone must work with all possible types, it returns Object. (We see later how to convert this result.) The class exposes two String properties, FirstName and LastName. You might remember that, although String is a reference type behind the scenes, you will actually treat it as a value type. The class also exposes another property named Work of type Job. This is a new reference type representing a person’s occupation. Job is implemented in the following way:

Class Job
    Property CompanyName As String
    Property Position As String
End Class

Given this implementation, we can simply create a shallow copy.

Shallow Copy

A shallow copy creates a new instance of the current object and copies values of members of the original to the new one but does not create copies of children (referenced) objects. Continuing the example of the preceding implementation, Clone creates a copy of the Person class into a new instance and copies members’ values that are value types or Strings. Because Job is a pure reference type, the shallow copy provided by Clone will not also create a clone of Job. This is the explanation about what we said before, that a shallow copy creates a copy only of the specified instance but not of children objects. We can easily verify our assertions by writing the following code:

Sub Main()

    'The original person
    Dim firstPerson As New Person
    firstPerson.FirstName = "Alessandro"
    firstPerson.LastName = "Del Sole"

    'Defines a work for the above person
    Dim randomJob As New Job
    randomJob.CompanyName = "Del Sole Ltd."
    randomJob.Position = "CEO"

    'Assignment of the new job
    firstPerson.Work = randomJob

    'Gets a shallow copy of the firstPerson object
    Dim secondPerson As Person = CType(firstPerson.Clone, Person)


    'Check if they are the same instances

    'returns False, 2 different instances:
    Console.WriteLine(firstPerson.FirstName Is secondPerson.FirstName)

    'returns True (still same instance of Job!):
    Console.WriteLine(firstPerson.Work Is secondPerson.Work)
    Console.ReadLine()
End Sub

The preceding code first gets a new instance of the Person class, setting some properties such as a new instance of the Job class, too. Notice how the result of the Clone method, which is of type Object, is converted into a Person instance using CType. At this point we can check what happened. The Is operator enables comparing two instances of reference types and returns True if they are related to the same instance. For the FirstName property, the comparison returns False because the shallow copy created a new, standalone instance of the Person class. But if we do the same check on the Work property, which is a child reference type of the Person class, the comparison returns True. This means that firstPerson.Work refers to the same instance of the Job class as in secondPerson.Work. And this also means that a shallow copy did not create a new copy of the Job class to be assigned to the secondPerson object. This is where the deep copy comes in.

Deep Copy

Deep copy is something complex that can create perfect copies of an entire object’s graph. If you need to perform a deep copy, you have some alternatives. The easiest (and the one we can show at this point of the book) is to perform a shallow copy of the main object and then manually copy the other properties of children reference types. The best one instead recurs to serialization, which is an advanced topic discussed in Chapter 41, “Serialization.” At the moment we can focus on editing the previous implementation of the Clone method for performing a simple deep copy. We could also implement the ICloneable interface in the Job class, as follows:

Class Job
    Implements ICloneable

    Property CompanyName As String
    Property Position As String

    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return Me.MemberwiseClone
    End Function
End Class

Now we can modify the Clone implementation inside the Person class, as follows:

Class Person
    Implements ICloneable

    Property FirstName As String
    Property LastName As String
    Property Work As Job

    Public Function Clone() As Object Implements System.ICloneable.Clone

        Dim tempPerson As Person = CType(Me.MemberwiseClone, Person)

        tempPerson.Work = CType(Me.Work.Clone, Job)

        Return tempPerson

    End Function
End Class


Implementing Icloneable

Implementing ICloneable in referenced classes is not the only way of providing deep copies. We could also generate a new instance of the Job class and manually assign values read from the original instance. But because we are discussing ICloneable and Clone, the example was completed this way.


The code obtains first a shallow copy of the current instance of the Person class and then gets a shallow copy of the child instance of the Job class.


Pay Attention to Recursive Clone

Cloning objects with recursive calls to the Clone method could lead to a stack overflow if the hierarchy of your objects is particularly complex. Because of this, the previous implementation goes well with small classes and small objects graphs. If this is not the case, you should prefer serialization.


If we now try to run the same check again comparing the instances of firstPerson and secondPerson, the output will be the following:

False
False

This is because now the instances are different. We have two completely standalone instances of the Person class.

The GetType Keyword

Each time you create an instance of a class, the .NET runtime creates an instance behind the scenes of the System.Type class that represents your object. Because in the .NET development you have the ability to inspect instances of the System.Type class (known as Reflection) at runtime and to get a reference to that System.Type, the Visual Basic programming language offers the GetType keyword that enables you to accomplish both tasks. The GetType keyword has two different behaviors: The first one is an operator, the second one is a method. GetType is typically used to compare two instances of an object or to access metadata of a type at runtime. To understand GetType, here are a few examples. Consider this first code snippet:

'GetType here is related to the type
Dim testType As Type = GetType(Integer)

For Each method As System.Reflection.MethodInfo
                In testType.GetMethods
    Console.WriteLine(method.Name)
Next
Console.ReadLine()

In a few words, the preceding code retrieves all the information and metadata related to the System.Int32 type; then it shows a list of all the methods exposed by such type, using Reflection. (MethodInfo is an object representing the methods’ information.) This is useful if you need to retrieve information on a particular data type. If you instead need to retrieve information about metadata of a specific instance of a data type, you can use the GetType method, as shown in the following code:

'GetType here is related to an instance
Dim testInt As Integer = 123456
Dim testType As Type = testInt.GetType

For Each method As System.Reflection.MethodInfo
                In testType.GetMethods
    Console.WriteLine(method.Name)
Next
Console.ReadLine()

In this particular situation, you retrieve information of an instance of the System.Int32 type and not of the type. You can also use GetType to compare two types for equality:

'Comparing two types
If testInt.GetType Is GetType(Integer) Then
    Console.WriteLine("TestInt is of type Integer")
End If

Understanding Conversion Operators

In the previous section we discussed converting between value types and reference types. These kinds of conversions are not the only ones allowed by the .NET Framework because you often need to perform conversions between two value types or two reference types. For example, imagine that you want to represent a number as a text message. In such a case, you need to convert an Integer into a String; you could also have an Integer value that must be passed to a method that instead receives an argument of type Double. Another common scenario is when you work with user controls in the user interface. Some controls such as ListBox and DataGridView on the Windows side or the DataGrid on the web side can store any kind of object. If you show a list of Person objects inside a ListBox, the control stores a list of Object, so you need to perform an explicit conversion from Object to Person each time you need to retrieve information on a single Person. In this section we show how conversions between types can be performed.

Widening and Narrowing Conversions

With the exception of boxing and unboxing, conversions are of two kinds: widening conversions and narrowing conversions. Their type depends on whether the conversion is explicit or implicit. This is explained next.

Widening Conversions

Widening conversions occur when you try to convert a type into another one that can include all values of the original type. A typical example of a widening conversion is converting from an Integer into a Double, as in the following lines of code:

'Widening conversion
Dim i As Integer = 1
Dim d As Double = i

As you might remember from Table 4.2, Integer represents a numeric value whereas Double represents a large floating number. Therefore, Double is greater than Integer and can accept conversions from Integer without loss of precision. Widening conversions do not need an explicit conversion operator, which instead happens for narrowing conversions.

Narrowing Conversions

The opposite of widening conversions, narrowing conversions occur when you attempt to convert a type into another that is smaller or with a loss of precision. For example, converting a Double into an Integer can cause a loss of precision because Double is a large floating number and Integer is just a numeric value, which is also smaller than Double. There are several ways to perform narrowing conversions, depending on how the types you need to convert are implemented. Visual Basic 2012, continuing what was already available in previous versions of the language, provides an easy way to perform conversions between base types wrapped by Visual Basic keywords. For example, if we need to convert a Double into an Integer, we could write the following lines of code:

Dim d As Double = 12345.678
Dim i As Integer = CInt(d)

The CInt function converts the specified Object into an Integer. In this particular case the value of i becomes 12346 because the conversion caused a loss of precision and the Visual Basic compiler produced an integer number that is the closest to the original value. Another example is when you need to convert a number into a string representation of the number itself. This can be accomplished by the following line of code:

Dim s As String = CStr(i)

The CStr function converts the specified Object into a string. With particular regard to string conversions, all .NET objects expose a method named ToString that performs a conversion of the original data into a new String. The last line of code could be rewritten as follows:

Dim s As String = i.ToString()

ToString is also useful because you can format the output. For example, consider the following line of code:

Dim i As Integer = 123456
Dim s As String = i.ToString("##,##.00")

The ToString method enables specifying how the string should be formatted. On my machine the code produces the following output:

123,456.00

The output depends on regional settings for your system, with particular regard to separators.


Use ToString Instead of CStr

Because it enables you to format the result, ToString should be preferred to CStr. You can override the standard implementation of ToString (provided by the Object class) so that you can provide your own conversion logic. This is discussed later, with regard to inheritance.


Table 4.5 shows the complete list of conversion functions provided by the Visual Basic grammar.

Table 4.5. Visual Basic Conversion Functions

Image

Remember the Loss of Precision

Always take care when performing narrowing conversions because of the loss of precision, particularly with numeric values. Another particular case is when converting from String to Char. Such conversions retrieve only the first character of the specified string.


When narrowing conversions fail, an InvalidCastException is thrown by the compiler. An example of this situation occurs when you attempt to convert a String into an Integer. Because String is a valid object expression, you do not get an error at compile time, but the conversion would fail at runtime. Because of this, you should always enclose conversions within error handling code blocks (see Chapter 6, “Handling Errors and Exceptions,” for details). There are also alternatives that are independent from the Visual Basic language and that are provided by the .NET Framework. The first way is using the System.Convert class that is available on objects that implement the IConvertible interface. Convert exposes a lot of methods, each for converting into a particular data type. For example, the following line of code converts a string representation of a number into an integer:

Dim c As Integer = System.Convert.ToInt32("1234")

If the string contains an invalid number, a FormatException is thrown. Methods exposed by the Convert class are well implemented because they do not only accept Object expressions, but also specific types, such as Integer, Boolean, String, and so on. IntelliSense can help you understand which types are supported. Table 4.6 provides an overview of the most common conversion methods exposed by System.Convert.

Table 4.6. System.Convert Most Commonly Used Methods

Image

Notice that System.Convert also provides other methods that are not discussed here because they are related to particular situations that assume you have a deep knowledge of specific .NET topics and are out of the scope in a general discussion such as this one.

CType, DirectCast, TryCast

The Visual Basic programming language offers some other conversion operators that are probably the most commonly used because of their flexibility. The first one is CType. It converts from one type to another and, if the conversion fails, the Visual Basic compiler throws an exception. The good news is that the conversion is also performed by the background compiler, so if the target type’s range exceeds the source one, you are immediately notified of the problem. For example, the compiler knows that converting a Date into an Integer is not possible, so in such a scenario you are immediately notified. If the conversion is legal (and therefore can compile) but types are populated at runtime with data that cannot be converted, an InvalidCastException is thrown. For example, the following code converts an Integer into a Short:

Dim i As Integer = 123456
Dim s As Short = CType(i, Short)

CType is also useful in unboxing. The following code converts from an Object that contains an Integer into a pure Integer:

Dim p As Object = 1
Dim result As Integer = CType(p, Integer)

Of course, it can also be used for converting between reference types:

Dim p As Object = New Person
Dim result As Person = CType(p, Person)

CType is specific to the Visual Basic runtime and enables widening and narrowing conversions from one type into another type that accepts the first one. For example, a Double can be converted to an Integer, although with loss of precision. Another couple of important operators, DirectCast and TryCast, can be used the same way, but they have different behavior. Both operators enable conversions when there is an inheritance or implementation relationship between the two types. For example, consider the following lines of code:

Dim d As Double = 123.456
Dim s As Short = CType(d, Short)

Converting from Double to Short using CType will succeed. Now consider the usage of DirectCast in the same scenario:

Dim d As Double = 123.456
Dim s As Short = DirectCast(d, Short)

This conversion will fail because Short does not inherit from Double and no implementation relationship exists between the two types. A conversion failure is notified via an InvalidCastException. This means you should use DirectCast only when you are sure that the inheritance or implementation conditions between types are satisfied. However, using DirectCast has some advantages in terms of performances because it relies directly on the .NET runtime. DirectCast conversions are also checked by the background compiler, so you are immediately notified if conversions fail via the Error List window. DirectCast works with both value and reference types. If you work with reference types, it’s a best practice to check whether the two types are compliant so you can reduce the risks of errors:

'In this example P is of type Object but stores a Person
If TypeOf (p) Is Person Then
    Dim result As Person = DirectCast(p, Person)
End If

The TypeOf operator compares an object reference variable to a data type and returns True if the object variable is compatible with the given type. As you might imagine, such checks can require performance overhead. Another operator that works only with reference types is known as TryCast. This one works exactly like DirectCast, but instead of throwing an InvalidCastException in the case of conversion failure, it returns a null object (Nothing). This can be useful because you can avoid implementing an exceptions check, simplifying your code (that will only need to check for a null value) and reducing performance overhead. The last code snippet could be rewritten as follows:

'In this example P is of type Object but stores a Person
Dim result As Person = TryCast(p, Person)

If the conversion fails, TryCast returns Nothing, so you will just need to check such a result.

Working with .NET Fundamental Types

There are special types that you will often work with, such as strings, date and time, and arrays. Although you will often work with collections, too (see Chapter 16, “Working with Collections and Iterators”), understanding how such objects work is an important objective. The .NET Framework 4.5 simplifies your developer life because objects provide methods to perform the most common operations on the data.


Note on Extension Methods

This section describes built-in methods from value and reference types. Because of the .NET infrastructure, all types provide the ability of invoking extension methods that could be potentially used for accomplishing some of the tasks proposed in this section. They will not be described because the scope of this chapter is to describe built-in members; you will need to understand extension methods, which are covered in Chapter 20.


Working with Strings

Working with strings is one of the most common developer activities. In the .NET Common Type System, System.String is a reference type. This might be surprising because strings actually behave like value types. The String class cannot be inherited, so you can’t create a custom class derived from it. In addition, String objects are immutable, like value types. What does this mean? It means that when you create a new String, you cannot change it. Although you are allowed to edit a string’s content, behind the scenes the CLR does not edit the existing string; it instead creates a new instance of the String object containing your edits. The CLR then stores such String objects in the Heap and returns a reference to them. We discuss how to approach strings in a more efficient way later; at the moment you need to understand how to work with them. The System.String classv provides lots of methods for working with strings without the need to write custom code. Assuming you understand the previous section relating to reference types, you can learn how to manipulate strings using the most common System.String methods.


System.String Methods

System.String provides several methods for performing operations on strings. We discuss the most important of them. Each method comes with several overloads. Discussing every overload is not possible, so you learn how methods work; then you can use IntelliSense, the Object Browser, and the documentation for further information.


Comparing Strings

Comparing the content of two strings is an easy task. The most common way for comparing strings is taking advantage of the equality (=) operator, which checks whether two strings have the same value. The following is an example that compares strings for equality:

Dim stringOne As String = "Hi guys"
Dim stringTwo As String = "How are you?"
Dim stringThree As String = "Hi guys"
'Returns False
Dim result1 As Boolean = (stringOne = stringTwo)
'Returns True
Dim result2 As Boolean = (stringOne = stringThree)

You can also use the equality operator inside conditional blocks, as in the following snippet:

If stringOne = stringTwo Then
    'Do something if the two strings are equal
End If

You instead check for strings’ inequality using the inequality operator (<>).


The Visual Basic Compiler and the Equality Operator

When using the equality operator for string comparisons, the Visual Basic compiler works differently from other managed languages. In fact, behind the scenes it makes a call to the Microsoft.VisualBasic.CompilerServices.Operators.CompareString method, whereas other languages, such as C#, make an invocation to System.String.Equals.


The String class also exposes other interesting methods for comparing strings: Equals, Compare, CompareTo, and CompareOrdinal. Equals checks for strings equality and returns a Boolean value of True if the strings are equal or False if they are not (which is exactly like the equality operator). The following code compares two strings and returns False because they are not equal:

Dim firstString As String = "Test string"
Dim secondString As String = "Comparison Test"

Dim areEqual As Boolean = String.Equals(firstString, secondString)

Equals has several signatures allowing deep control of the comparison. For example, you could check if two strings are equal according to the local system culture and without being case-sensitive:

Dim areCaseEqual As Boolean =
    String.Equals(firstString, secondString,
    StringComparison.CurrentCultureIgnoreCase)

The StringComparison object provides a way for specifying comparison settings. IntelliSense provides descriptions for each available option. Then there is the Compare method. It checks whether the first string is minor, equal, or greater than the second and returns an Integer value representing the result of the comparison. If the first string is minor, it returns -1; if it is equal to the second one, the method returns zero; if the first string is greater than the second, it returns 1. The following code snippet demonstrates this kind of comparison:

Dim firstString As String = "Test string"
Dim secondString As String = "Comparison Test"

Dim result As Integer = String.Compare(firstString, secondString)

In this case Compare returns 1 because the second string is greater than the first one. Compare enables specifying several comparing options. For example, you could perform the comparison based on case-sensitive strings. The following code demonstrates this:

Dim caseComparisonResult As Integer =
    String.Compare(firstString, secondString, True)

For Equals, Compare also enables a comparison based on other options, such as the culture information of your system. The next method is String.CompareTo, whose return values are the same as String.Compare; however, String.CompareTo is an instance method. You use it like in the following code:

Dim firstString As String = "Test string"
Dim secondString As String = "Comparison Test"

Dim result As Integer = firstString.CompareTo(secondString)

The last valuable method is String.CompareOrdinal, which checks for casing differences via ordinal comparison rules, which means comparing the numeric values of the corresponding Char objects that the string is composed of. The following is an example:

Dim firstString As String = "test"
Dim secondString As String = "TeSt"

'Returns:
'0 if the first string is equal to the second
'< 0 if the first string is less than the second
'> 0 if the first string is greater than the second
Dim result As Integer = String.CompareOrdinal(firstString, secondString)

Checking for Empty or Null Strings

The System.String class provides a method named IsNullOrEmpty that easily enables checking if a string is null or if it does not contain any characters. You can use such a method as follows:

If String.IsNullOrEmpty(stringToCheck) = False Then
    'The string is neither null nor empty
Else
    'The string is either null or empty
End If

Of course, you could also perform your check against True instead of False. In such situations both conditions (null or empty) are evaluated. This can be useful because you often need to validate strings to check whether they are valid. There could be situations in which you need to just ensure that a string is null or not empty. In this case you should use the usual syntax:

If stringToCheck Is Nothing Then
    'String is null
End If
If stringToCheck = "" Then
    'String is empty
End If

Formatting Strings

Often you need to send output strings according to a particular format, such as currency, percentage, and decimal numbers. The System.String class offers a useful method named Format that enables you to easily format text. Consider the following code example, paying attention to comments:

'Returns "The cost for traveling to Europe is $1,000.00
Console.WriteLine(String.Format("The cost for traveling to Europe is {0:C}
                                 dollars", 1000))
'Returns "You are eligible for a 15.50% discount"
Console.WriteLine(String.Format("You are eligible for a {0:P} discount",
                                15.55F))
'Returns "Hex counterpart for 10 is A"
Console.WriteLine(String.Format("Hex counterpart for 10 is {0:X}", 10))

The first thing to notice is how you present your strings; Format accepts a number of values to be formatted and then embedded in the main string, which are referenced with the number enclosed in brackets. For example, {0} is the second argument of Format, {1} is the third one, and so on. Symbols enable the format; for example, C stands for currency, P stands for percentage, and X stands for hexadecimal. Visual Basic 2012 offers the symbols listed in Table 4.7.

Table 4.7. Format Symbols Accepted

Image

Roundtrip

Roundtrip ensures that conversions from floating point to String and that converting back are allowed.


You can format multiple strings in one line of code, as in the following example:

Console.Writeline(String.Format("The traveling cost is" &
                  " {0:C}. Hex for {1} is '{1,5:X}'", 1000, 10))

The preceding code produces the following result:

The traveling cost is $1,000.00. Hex for 10 is '    A'

As you can see, you can specify a number of white spaces before the next value. This is accomplished by typing the number of spaces you want to add followed by a : symbol and then the desired format symbol. String.Format also enables the use of custom formats. Custom formats are based on the symbols shown in Table 4.8.

Table 4.8. Symbols You Can Use for Custom Formats

Image

According to Table 4.7, we could write a custom percentage representation:

'Returns "Custom percentage %1,550"
Console.WriteLine(String.Format("Custom percentage {0:%##,###.##} ", 15.50))

Or you could write a custom currency representation. For example, if you live in Great Britain, you could write the following line for representing the Sterling currency:

Console.WriteLine(String.Format("Custom currency {0:£#,###.00} ", 987654))

Another interesting feature in customizing output is the ability to provide different formats according to the input value. For example, you can decide to format a number depending if it is positive, negative, or zero. At this regard, consider the following code:

Dim number As Decimal = 1000
Console.WriteLine(String.
                Format("Custom currency formatting:
                {0:£#,##0.00;*£#,##0.00*;Zero}",
                number))

Here you specify three different formats, separated by semicolons. The first format affects positive numbers (such as the value of the number variable); the second one affects negative numbers, and the third one affects a zero value. The preceding example therefore produces the following output:

Custom currency formatting: £1,000.00

If you try to change the value of the number to −1000, the code produces the following output:

Custom currency formatting: *£1,000.00*

Finally, if you assign number = 0, the code produces the following output:

Custom currency formatting: Zero

Creating Copies of Strings

Strings in .NET are reference types. Because of this, assigning a string object to another string to perform a copy will just copy the reference to the actual string. Fortunately, the System.String class provides two useful methods for copying strings: Copy and CopyTo. The first one creates a copy of an entire string:

Dim sourceString As String = "Alessandro Del Sole"
Dim targetString As String = String.Copy(sourceString)

Copy is a shared method and can create a new instance of String and then put into the instance the content of the original string. If you instead need to create a copy of only a subset of the original string, you can invoke the instance method CopyTo. Such method works a little differently from Copy because it returns an array of Char. The following code provides an example:

Dim sourceString As String = "Alessandro Del Sole"
Dim charArray(sourceString.Length) As Char
sourceString.CopyTo(11, charArray, 0, 3)
Console.WriteLine(charArray)

You first need to declare an array of Char—in this case as long as the string length. The first argument of CopyTo is the start position in the original string; the second is the target array. The third one is the start position in the target array, and the fourth one is the number of characters to copy. In the end, such code produces Del as the output.


Clone Method

The String class also offers a method named Clone. You should not confuse this method with Copy and CopyTo because it will just return a reference to the original string and not a real copy.


Inspecting Strings

When working with strings, you often need to inspect or evaluate their content. The System.String class provides both methods and properties for inspecting strings. Imagine you have the following string:

Dim testString As String = "This is a string to inspect"

You can retrieve the string’s length via its Length property:

'Returns 27
Dim length As Integer = testString.Length

Another interesting method is Contains, which enables you to know whether a string contains the specified substring or array of Char. Contains returns a Boolean value, as you can see in the following code snippet:

'Returns True
Dim contains As Boolean = testString.Contains("inspect")
'Returns False
Dim contains1 As Boolean = testString.Contains("Inspect")

Just remember that evaluation is case-sensitive. In some situations, you might need to check whether a string begins or ends with a specified substring. You can verify both situations using StartsWith and EndsWith methods:

'Returns False, the string starts with "T"
Dim startsWith As Boolean = testString.StartsWith("Uh")
'Returns True
Dim endsWith As Boolean = testString.EndsWith("pect")

Often you might also need to get the position of a specified substring within a string. To accomplish this, you can use the IndexOf method. For example, you could retrieve the start position of the first “is” substring as follows:

'Returns 2
Dim index As Integer = testString.IndexOf("is")

The code returns 2 because the start index is zero-based and refers to the “is” substring of the “This” word. You do not need to start your search from the beginning of the string; you can specify a start index or specify how the comparison must be performed via the StringComparison enumeration. Both situations are summarized in the following code:

'Returns 5
Dim index1 As Integer = testString.IndexOf("is", 3,
                        StringComparison.InvariantCultureIgnoreCase)


StringComparison Enumeration

You can refer to IntelliSense when typing code for further details on the StringComparison enumeration options. They are self-explanatory, and for the sake of brevity, all options cannot be shown here.


IndexOf performs a search on the exact substring. You might also need to search for the position of just one character of a set of characters. This can be accomplished using the IndexOfAny method as follows:

'Returns 1
Dim index2 As Integer = testString.
                        IndexOfAny(New Char() {"h"c, "s"c, "i"c})

The preceding code has an array of Char storing three characters, all available in the main string. Because the first character in the array is found first, IndexOfAny returns its position. Generally, IndexOfAny returns the position of the character that is found first. There are counterparts to both IndexOf and IndexOfAny: LastIndexOf and LastIndexOfAny. The first two methods perform a search starting from the beginning of a string, whereas the last two perform a search starting from the end of a string. This is an example:

'Returns 5
Dim lastIndex As Integer = testString.LastIndexOf("is")
'Returns 22
Dim lastIndex1 As Integer = testString.LastIndexOfAny(New Char()
                            {"h"c, "s"c, "i"c})

Notice how LastIndexOf returns the second occurrence of the “is” substring if you consider the main string from the beginning. Indexing is useful, but this only stores the position of a substring. If you need to retrieve the text of a substring, you can use the SubString method that works as follows:

'Returns "is a string"
Dim subString As String = testString.Substring(5, 11)

You can also just specify the start index, if you need the entire substring starting from a particular point.

Editing Strings

The System.String class provides members for editing strings. The first method described is named Insert and enables adding a substring into a string at the specified index. Consider the following example:

Dim testString As String = "This is a test string"
'Returns
'"This is a test,for demo purposes only,string"
Dim result As String = testString.Insert(14, ",for demo purposes only,")

As you can see from the comment in the code, Insert adds the specified substring from the specified index but does not append or replace anything. Insert’s counterpart is Remove, which enables removing a substring starting from the specified index or a piece of substring from the specified index and for the specified number of characters. This is an example:

'Returns "This is a test string"
Dim removedString As String = testString.Remove(14)

Remember that you always need to assign the result of a method call that makes an edit to a string, because of the immutability of string types. In this case, invoking Remove does not change the content of testString, so you need to assign the result of the method call to a variable (new or existing).

Another common task is replacing a substring within a string with another string. For example, imagine you want to replace the “test” substring with the “demo” substring within the testString instance. This can be accomplished using the Replace method as follows:

'Returns
'"This is a demo string"
Dim replacedString As String = testString.Replace("test", "demo")

The result of Replace must be assigned to another string to get the desired result. (See “Performance Tips” at the end of this section.) Editing strings also contain splitting techniques. You often need to split one string into multiple strings, especially when the string contains substrings separated by a symbol. For example, consider the following code in which a string contains substrings separated by commas, as in CSV files:

Dim stringToSplit As String = "Name,Last Name,Age"

You might want to extract the three substrings, Name, Last Name, and Age, and store them as unique strings. To accomplish this, you can use the Split method, which can receive as an argument the separator character:

Dim result() As String = stringToSplit.Split(","c)
For Each item As String In result
    Console.WriteLine(item)
Next

The preceding code retrieves three strings that are stored into an array of String and then produces the following output:

Name
Last Name
Age

Split has several overloads that you can inspect with IntelliSense. One of these enables you to specify the maximum number of substrings to extract and split options, such as normal splitting or splitting if substrings are not empty:

Dim result() As String = stringToSplit.Split(New Char() {","c}, 2,
                StringSplitOptions.RemoveEmptyEntries)

In this overload you have to explicitly specify an array of Char; in this case there is just a one-dimension array containing the split symbol. Such code produces the following output, considering that only two substrings are accepted:

Name
Last Name, Age

Opposite to Split, there is also a Join method that enables joining substrings into a unique string. Substrings are passed as an array of String and are separated by the specified character. The following code shows an example:

'Returns "Name, Last Name, Age"
Dim result As String = String.Join(",",
                       New String() {"Name", "Last Name", "Age"})

Another way to edit strings is trimming. Imagine you have a string containing white spaces at the end of the string or at the beginning of the string, or both. You might want to remove white spaces from the main string. The System.String class provides three methods, Trim, TrimStart, and TrimEnd, that enable you to accomplish this task, as shown in the following code (see comments):

Dim stringWithSpaces As String = "    Test with spaces    "
'Returns "Test with spaces"
Dim result1 As String = stringWithSpaces.Trim
'Returns "Test with spaces   "
Dim result2 As String = stringWithSpaces.TrimStart
'Returns "   Test with spaces"
Dim result3 As String = stringWithSpaces.TrimEnd

All three methods provide overloads for specifying characters different from white spaces. (Imagine you want to remove an asterisk.) Opposite to TrimStart and TrimEnd, System.String exposes PadLeft and PadRight. The best explanation for both methods is a practical example. Consider the following code:

Dim padResult As String = testString.PadLeft(30, "*"c)

It produces the following result:

*********This is a test string

PadLeft creates a new string, whose length is the one specified as the first argument of the method and that includes the original string with the addition of a number of symbols that is equal to the difference from the length you specified and the length of the original string. In our case, the original string is 21 characters long, although we specified 30 as the new length. So, there are 9 asterisks. PadRight does the same, but symbols are added on the right side, as in the following example:

Dim padResult As String = testString.PadRight(30, "*"c)

This code produces the following result:

This is a test string*********

Both methods are useful if you need to add symbols to the left or to the right of a string.


Performance Tips

Because of its particular nature, each time you edit a string you are not actually editing the string—instead, you are creating a new instance of the System.String class. As you might imagine, this could lead to performance issues. That said, although it’s fundamental to know how you can edit strings; you should always prefer the StringBuilder object, especially when concatenating strings. StringBuilder is discussed later in this chapter.


Concatenating Strings

Concatenation is perhaps the most common task that developers need to perform on strings. In Visual Basic 2012 you have some alternatives. First, you can use the addition operator:

Dim firstString As String = "Hello! My name is "
Dim secondString As String = "Alessandro Del Sole"

Dim result As String = firstString + secondString

Another, better, approach is the String.Concat method:

Dim concatResult As String =
                    String.Concat(firstString, secondString)

Both ways produce the same result, but both ways have a big limitation; because strings are immutable, the CLR needs to create a new instance of the String class each time you perform a concatenation. This scenario can lead to a significant loss of performance. If you need to concatenate 10 strings, the CLR creates 10 instances of the String class. Fortunately, the .NET Framework provides a more efficient way for concatenating strings: the StringBuilder object.

The StringBuilder Object

The System.Text.StringBuilder class provides an efficient way for concatenating strings. You should always use StringBuilder in such situations. The real difference is that StringBuilder can create a buffer that grows along with the real needs of storing text. (The default constructor creates a 16-byte buffer.) Using the StringBuilder class is straightforward. Consider the following code example:

'Requires an Imports System.Text directive
Function ConcatenatingStringsWithStringBuilder() As String
        Dim result As New StringBuilder

        'Ensures that the StringBuilder instance
        'has the capacity of at least 100 characters
        result.EnsureCapacity(100)
        result.Append("Hello! My name is ")
        result.Append("Alessandro Del Sole")
        Return result.ToString
End Function

You simply instantiate the StringBuilder class using the New keyword and then invoke the Append method that receives as an argument the string that must be concatenated. In the end, you need to explicitly convert the StringBuilder to a String invoking the ToString method. This class is powerful and provides several methods for working with strings, such as AppendLine (which appends an empty line with a carriage return), AppendFormat (which enables you to format the appended string), and Replace (which enables you to replace all occurrences of the specified string with another string). The EnsureCapacity method used in the code example ensures that the StringBuilder instance can contain at least the specified number of characters. You can find in the StringBuilder class the same methods provided by the String class (Replace, Insert, Remove, and so on), so working with StringBuilder should be familiar and straightforward.

Working with Dates

Together with strings, you often need to handle dates and moments in time. To accomplish this, the .NET Framework 4.5 provides the System.DateTime value type.


MinValue and MaxValue

Being a value type, System.DateTime has two shared fields: MinValue and MaxValue. These store the minimum accepted date and the maximum date, respectively. The minimum date is 01/01/0001 00:00:00 a.m., and maximum date is 12/31/9999 11:59:59 p.m.


Creating Dates

The Visual Basic grammar offers the Date keyword, which is a lexical representation of the System.DateTime object, so you can use both definitions. For consistency, we use the Date reserved keyword, but keep in mind that this keyword creates (or gets a reference to) an instance of the System.DateTime type. Working with dates is an easy task. You can create a new date by creating an instance of the DateTime class:

Dim myBirthDate As New Date(1977, 5, 10)

The constructor has several overloads, but the most common is the preceding one, where you can specify year, month, and day. For example, such values could be written by the user and then converted into a DateTime object. Another common situation in which you need to create a date is for storing the current system clock date and time. This can be easily accomplished using the DateTime.Now property as follows:

Dim currentDate As Date = Date.Now

Such code produces the following result, representing the moment when I’m writing this chapter:

5/28/2012 10:02:35 PM

When you get an instance of a DateTime, you can retrieve a lot of information about it. For example, consider the following code taking care of comments:

'Creates a new date; May 10th 1977, 8.30 pm
Dim myBirthDate As New Date(1977, 5, 10,
                            20, 30, 0)

'In 1977, May 10th was Tuesday
Console.WriteLine(myBirthDate.DayOfWeek.
                  ToString)

'8.30 pm
Console.WriteLine("Hour: {0}, Minutes: {1}",
                  myBirthDate.Hour,
                  myBirthDate.Minute)

'Is the date included within the Day Light Saving Time period?
Console.WriteLine("Is Day light saving time: {0}",
                  myBirthDate.IsDaylightSavingTime.
                  ToString)

'Is leap year
Console.WriteLine("Is leap: {0}",
                  Date.IsLeapYear(myBirthDate.Year).
                  ToString)

The code first creates the following date, representing my birth date: 5/10/1977 8:30:00 PM. Then it retrieves some information, such as the name of the day of the week (represented by the DayOfWeek enumeration), the hours, the minutes (via the Hour and Minute integer properties), and the specified date within Daylight Saving Time. The DateTime object also exposes a shared method named IsLeapYear that can establish whether the specified year is a leap year. In our example, the year is not passed directly but is provided via the Year property of the myBirthDate instance. The following is the result of the code:

Tuesday
Hour: 20, Minutes: 30
Is Day light saving time: True
Is leap: False

Finally, you can declare dates with the so-called date literals. The following is an example of how you can customize the date format:

Dim customDate As Date = #5/25/2012 8:00:00 PM#

Converting Strings into Dates

It is not unusual to ask the user to provide a date within an application. Typically, this is accomplished via the user interface and, if you do not provide a specific user control (such as the WPF DatePicker or the Win Forms DateTimePicker) for selecting dates in a graphical fashion, such input is provided in the form of a string. Because of this, you need a way of converting the string into a date, so that you can then manipulate the user input as an effective DateTime object (unless the string is invalid; then you need validation). To accomplish this kind of conversion, the System.DateTime class provides two methods that you already saw when discussing value types: Parse and TryParse. For example, consider the following code that receives an input by the user and attempts to convert such input into a DateTime object:

Sub ParsingDates()
    Console.WriteLine("Please specify a date:")
    Dim inputDate As Date
    Dim result As Boolean = Date.TryParse(Console.ReadLine, inputDate)

    If result = False Then
        Console.WriteLine("You entered an invalid date")
    Else
        Console.WriteLine(inputDate.DayOfWeek.ToString)
    End If
End Sub

The TryParse method receives the string to convert as the first argument (which in this case is obtained by the Console window) and the output object passed by reference (inputDate); it returns True if the conversion succeeds or False if it fails. The conversion succeeds if the input string format is accepted and recognized by the DateTime type. If you run this code and enter a string in the format 1977/05/10, the conversion succeeds because such format is accepted by DateTime. You can then manipulate the new date as you like. (In the preceding example, the code shows the day of the week for the specified date, which in my example is Tuesday.)


Tip

In many cases you work with data from a database. The ADO.NET engine and layered technologies, such as LINQ, map dates from databases directly into a System.DateTime object so that you will be able to work and manipulate such objects from and to data sources.


Formatting Dates

You need to present dates for several scenarios, and you might be required to perform this task in different ways. Fortunately, the System.DateTime provides many ways of formatting dates. The easiest way is invoking the ToString method, which accepts an argument that enables specifying how a date must be presented. For example, consider the following code snippet that writes the current date in both the extended (D) and the short (d) date formats:

Console.WriteLine(DateTime.Now.ToString("D"))
Console.WriteLine(DateTime.Now.ToString("d"))

Such code produces the following output:

Monday, May 28, 2012
5/28/2012

The result is based on the regional and culture settings of your system. Table 4.9 summarizes symbols you can use with the ToString method.

Table 4.9. Date Formatting Symbols with ToString

Image

ToString also recognizes date literals. The following is an example of how you can customize and write a date:

Console.WriteLine(Date.Today.ToString("dd/MM/yyyy"))

The previous code prints the current date in the Day/Month/Year format. System.DateTime provides a plethora of other useful methods you can use for formatting dates. Such methods also return different data types, depending on the scenario in which they have to be used in. Table 4.10 summarizes the most important methods you can always inspect with IntelliSense and the Object Browser.

Table 4.10. System.DateTime Useful Methods

Image

The following code snippet uses all the preceding methods to demonstrate how the output differs depending on the method:

Console.WriteLine("Local time: {0}", Date.Now.ToLocalTime)
Console.WriteLine("Long date: {0}", Date.Now.ToLongDateString)
Console.WriteLine("Short date: {0}", Date.Now.ToShortDateString)
Console.WriteLine("Long time: {0}", Date.Now.ToLongTimeString)
Console.WriteLine("Short time: {0}", Date.Now.ToShortTimeString)
Console.WriteLine("Universal time: {0}", Date.Now.
                  ToUniversalTime.ToString)
Console.WriteLine("File time: {0}", Date.Now.
                  ToFileTime.ToString)
Console.WriteLine("File time UTC: {0}", Date.Now.
                  ToFileTimeUtc.ToString)
Console.WriteLine("OLE Automation date: {0}", Date.Now.
                  ToOADate.ToString)

The preceding code produces the following result, which you can compare with the methods described in Table 4.9:

Local time: 05/28/2012 19:27:22
Long date: Monday, May 28, 2012
Short date: 5/28/2012
Long time: 7:27:22 PM
Short time: 7:27 PM
Universal time: 5/28/2012 7:27:22 PM
File time: 129826997181439508
File time UTC: 129826997181439508
OLE Automation date: 41057.811552581

Subtracting Dates and Adding Time to Time

It’s not unusual to need to know the amount of time spent between two dates. The System.DateTime enables this by invoking a Subtract method, which returns a System.TimeSpan value. For example, consider the following code that subtracts a date from another one:

Dim birthDate As Date = New Date(1977, 5, 10, 20, 30, 0)
Dim secondDate As Date = New Date(1990, 5, 11, 20, 10, 0)

Dim result As System.TimeSpan = secondDate.Subtract(birthDate)

'In days
Console.WriteLine(result.Days)
'In "ticks"
Console.WriteLine(result.Ticks)

You can subtract two DateTime objects and get a result of type TimeSpan (discussed next). You can then get information on the result, such as the number of days that represent the difference between the two dates or the number of ticks. The previous code produces the following result:

4748
4103124000000000

You can also add values to a date. For example you can edit a date by adding days, hours, minutes, seconds, or ticks or by incrementing the year. Consider the following code snippet:

Dim editedDate As Date = birthDate.AddDays(3)
editedDate = editedDate.AddHours(2)
editedDate = editedDate.AddYears(1)

Console.WriteLine(editedDate)

This code adds three days and two hours to the date and increments the year by one unit. In the end, it produces the following output:

5/13/1978 10:30:00 PM

You can use negative numbers to subtract values from a single date, which returns a Date object instead of a TimeSpan like in Subtract. For instance, the following code subtracts a day from the date represented by the birthDate variable:

'Returns 05/09/1977
Dim result As Date = birthDate.AddDays(-1)

Dates are important and, although they allow working with time, too, the .NET Framework provides an important structure specific for representing pieces of time: System.TimeSpan.


Note About Operators

You can use standard operators such as the addition and subtraction operators when working with both DateTime and TimeSpan objects. This is possible because both objects overload the standard operators. Overloading operators is discussed in Chapter 11.


Working with Time

You often need to represent intervals of time in your applications, especially in conjunction with dates. The .NET Framework provides a structure—a value type named System.TimeSpan. This structure can represent time from a minimum value (one tick) until a maximum value (one day). A tick is the smallest unit for time representations and is equal to 100 nanoseconds. TimeSpan represents a summed amount of time between two given time values, and the time portion of a Date object represents a single specific moment in time.


Minimum and Maximum Values

As for other value types, System.TimeSpan also provides two shared properties named MinValue and MaxValue. MinValue returns -10675199.02:48:05.4775808, and MaxValue returns 10675199.02:48:05.4775807. For the sake of clarity, both values are respectively equal to System.Int64.MinValue and System.Int64.MaxValue.


You can find several places in which using TimeSpan is needed other than simply working with dates. For example, you might want to create your performance benchmarks using the StopWatch object that returns a TimeSpan. Or you might need such structure when working with animations in WPF applications. The following code example simulates a performance test; a System.StopWatch object is started, an intensive loop is performed, and then the StopWatch is stopped. The StopWatch class offers an Elapsed property that is of type TimeSpan and that can be useful for analyzing the amount of elapsed time:

Dim watch As New Stopwatch

watch.Start()
For i = 0 To 10000
    'Simulates intensive processing
    System.Threading.Thread.SpinWait(800000)
Next
watch.Stop()

Console.WriteLine(watch.Elapsed.Seconds)
Console.WriteLine(watch.Elapsed.Milliseconds)
Console.WriteLine(watch.Elapsed.Ticks)

The preceding code produced the following result on my machine, but it will be different on yours, depending on your hardware:

49
374
493746173

The TimeSpan structure is similar to the area of the DateTime type that is related to time. Notice that TimeSpan offers several similar properties, such as Days; Hours; Minutes; Seconds; Milliseconds; and methods such as AddDays, AddHours, AddMinute, and Subtract. TimeSpan is all about time; this means that although there are similarities, as mentioned before, with the time-related DateTime members, you cannot (obviously) work with dates. The following code provides an example of creating a TimeSpan instance starting from an existing date:

Sub TimeSpanInstance()
    Dim currentDate As Date = Date.Now
    'Because the System namespace is imported at project
    'level, we do not need an Imports directive
    Dim intervalOfTime As TimeSpan = currentDate.TimeOfDay

    Console.WriteLine("My friend, in the current date " &
                      "there are {0} days; time is {1}:{2}:{3}",
            intervalOfTime.Days,
            intervalOfTime.Hours,
            intervalOfTime.Minutes,
            intervalOfTime.Seconds)
End Sub

The preceding code produces the following result:

My friend, in the current date there are 0 days; time is 19:30

In the specified interval, there is only the current day, so the first argument returns zero. Take a look back at the section “Subtracting Dates and Adding Time to Time” to see an example of TimeSpan usage for an interval of time retrieved subtracting two dates.

Working with TimeZone and TimeZoneInfo

You might often ask what people are doing on the other side of world when in your country it’s a particular time of the day. Working with time zones can also be important for your business if you need to contact people who live in different and faraway countries. The .NET Framework provides two types, TimeZone and TimeZoneInfo, which enable retrieving information on time zones. Both types are exposed by the System namespace. For example, imagine you want to retrieve information on the time zone of your country. This can be accomplished as follows (assuming regional settings on your machine are effectively related to your country):

Dim zone As TimeZone = TimeZone.CurrentTimeZone

TimeZone is a reference type and through its CurrentTimeZone property it provides a lot of information such as the name of the time zone or the Daylight Saving Time period as demonstrated here:

Console.WriteLine(zone.DaylightName)
Console.WriteLine(zone.StandardName)
Console.WriteLine(zone.IsDaylightSavingTime(Date.Now))

This code produces the following result on my machine:

W. Europe Daylight Time
W. Europe Standard Time
True

The official MSDN documentation states that using the TimeZoneInfo class should be preferred instead of TimeZone. This is because TimeZoneInfo also provides the ability of creating custom time zones. The following code shows how you can retrieve current time zone information using TimeZoneInfo:

Dim tz As TimeZoneInfo = TimeZoneInfo.Local
'Shows the current time zone Identifier
Console.WriteLine(tz.Id)

Creating a custom time zone is also a simple task, which is accomplished by the following code:

Dim customZone As TimeZoneInfo = TimeZoneInfo.
    CreateCustomTimeZone("CustomTimeZone",
    Date.UtcNow.Subtract(Date.Now),
    "Custom Zone", "Custom Zone")

All you need is to specify a custom identifier, the difference between the UTC time span and the local time span, a daylight identifier, and a standard identifier. TimeZoneInfo also provides another useful method for enumerating time zones recognized by the system, named GetSystemTimeZones; you can use it like this:

For Each timez As TimeZoneInfo In TimeZoneInfo.GetSystemTimeZones
    Console.WriteLine(timez.DisplayName)
Next

An excerpt of the output provided by this simple iteration is the following:

(UTC-12:00) International Date Line West
(UTC-11:00) Midway Island, Samoa
(UTC-10:00) Hawaii
(UTC-09:00) Alaska
(UTC-08:00) Pacific Time (US & Canada)
(UTC-08:00) Tijuana, Baja California
(UTC-07:00) Arizona
(UTC-07:00) Chihuahua, La Paz, Mazatlan
(UTC-07:00) Mountain Time (US & Canada)
(UTC-06:00) Central America
(UTC-06:00) Central Time (US & Canada)
(UTC-05:00) Eastern Time (US & Canada)
(UTC-05:00) Indiana (East)
(UTC-04:30) Caracas
(UTC-04:00) Santiago
(UTC-03:30) Newfoundland
(UTC-01:00) Cape Verde Is.
(UTC) Casablanca
(UTC) Coordinated Universal Time
(UTC) Dublin, Edinburgh, Lisbon, London
(UTC) Monrovia, Reykjavik
(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
(UTC+02:00) Windhoek
(UTC+03:00) Baghdad
(UTC+03:00) Kuwait, Riyadh
(UTC+03:00) Moscow, St. Petersburg, Volgograd
(UTC+03:00) Nairobi
(UTC+06:00) Almaty, Novosibirsk
(UTC+06:00) Astana, Dhaka
(UTC+06:30) Yangon (Rangoon)
(UTC+07:00) Krasnoyarsk
(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi
(UTC+08:00) Perth
(UTC+08:00) Taipei
(UTC+09:00) Yakutsk
(UTC+09:30) Adelaide
(UTC+10:00) Vladivostok
(UTC+11:00) Magadan, Solomon Is., New Caledonia
(UTC+12:00) Auckland, Wellington
(UTC+12:00) Fiji, Kamchatka, Marshall Is.
(UTC+13:00) Nuku'alofa

Thanks to this information, you could use the TimeZoneInfo class for converting between time zones. The following code demonstrates how to calculate the hour difference between Italy and Redmond, Washington:

'Redmond time; requires specifying the Time Zone ID
Dim RedmondTime As Date = TimeZoneInfo.
    ConvertTimeBySystemTimeZoneId(DateTime.Now, "Pacific Standard Time")

Console.WriteLine("In Italy now is {0} while in Redmond it is {1}",
                  Date.Now.Hour,RedmondTime.Hour)

By invoking the ConvertTimeBySystemZoneId method, you can convert between your local system time and another time, based on the zone ID. If you don’t know zone IDs, just replace the previous iterations for showing the content of the timez.Id property instead of DisplayName.

Working with GUIDs

How many times do you need to represent something with a unique identifier? Probably very often. Examples are items with the same name but with different characteristics. The .NET Framework enables creating unique identifiers via the System.Guid structure (therefore a value type). This lets you generate a unique, 128-bit string, identifier. To generate a unique identifier, invoke the Guid.NewGuid method, which works as follows:

'Declaring a Guid
Dim uniqueIdentifier As Guid

'A unique identifier
uniqueIdentifier = Guid.NewGuid
Console.WriteLine(uniqueIdentifier.ToString)

'Another unique identifier,
'although to the same variable
uniqueIdentifier = Guid.NewGuid
Console.WriteLine(uniqueIdentifier.ToString)

If you run the preceding code, you notice that each time you invoke the NewGuid method a new GUID is generated, although you assign such a value to the same variable. This makes sense because you use the GUID each time you need a unique identifier. On my machine the preceding code produces the following result:

46e0daca-db56-45b1-923e-f76cf1636019
6fd3a61d-ec17-4b6a-adec-6acfa9a0cb00


Examples of GUIDs

The Windows operating system makes a huge usage of GUIDs. If you try to inspect the Windows Registry, you’ll find lots of examples.


The Guid.NewGuid method provides auto-generated GUIDs. If you need to provide your own GUID, you can use the New constructor followed by the desired identifier:

'Specifying a Guid
uniqueIdentifier = New Guid("f578c96b-5918-4f79-b690-6c463ffb2c3e")

The constructor has several overloads, which enable generating GUIDs also based on byte arrays and integers. Generally, you use GUIDs each time you need to represent something as unique. Several .NET types accept GUIDs, so you need to know how you can create them in code, although this is not the only way.

Creating GUIDs with the Visual Studio Instrumentation

The Visual Studio IDE provides a graphical tool for generating GUIDs. You can run this tool by selecting the Create GUID command from the Tools menu. Figure 4.11 shows the Create GUID window.

Image

Figure 4.11. The Create GUID tool from Visual Studio.

You can choose the format you need for your GUID, such as Windows-like GUIDs. When you get your GUID, you can copy it to the clipboard and then reuse it in your code.

Working with Arrays

As a developer, you know what arrays are. They are a place in which you can store a set of items, generally of the same type. In the .NET Framework 4.5 (as in older versions), arrays are reference types all deriving from the System.Array class and can be both one-dimensional and multidimensional.


Arrays Versus Collections

Collections, especially generic ones, are more efficient than arrays. I always recommend working with collections instead of arrays, except when strictly needed. (For example, you might need jagged arrays.)


You can either declare an array and use it later in your code or declare it and assign it with objects. The following is an example of declaring an array of String objects, meaning that it can store only objects of type String:

Dim anArrayOfString() As String

In this line of code an array of String is declared. This array has no predefined bounds, so it is flexible and useful if you cannot predict how many items it needs to store. There is also an alternative syntax, which allows placing parentheses at the end of the type, as follows:

'Alternative syntax
Dim anArrayOfString As String()

You are free to use whichever syntax you like. For the sake of consistency, the first one is used.


Is Option Strict On?

The preceding and the following code examples assume that Option Strict is set to On. If it is off, adding instances of System.Object is also allowed but might cause errors at runtime. It is recommended to set Option Strict to On to avoid implicit conversion, and this is one of those cases.


You can initialize arrays directly when declaring them, as in the following code that declares an array of strings and stores three instances of the System.String class:

'Inline initialization with implicit bounds
Dim anArrayOfThreeStrings() As String = New String() {"One", "Two", "Three"}

Notice how you assign an array using the New keyword followed by the type name and a couple of parentheses. Brackets contain the items to store. The declared array has no bounds limits, but actually after the assignment its upper bound is 2, so its bounds are determined by the number of values it is initialized with.


Arrays Base

Arrays are zero-based. This means that an array with an upper bound of 2 can store three items (index of zero, index of one, and index of two). This also means that the upper bound will always be one less than the Count property of the array.


This approach works with arrays of other .NET types, too, as in the following code in which Char and Byte are used:

Dim anArrayOfChar() As Char = New Char() {"a"c, "b"c, "c"c}

Dim anArrayOfByte() As Byte = New Byte() {1, 2, 3}

If you already know how many items the array can store, you can specify the bounds limits. For example, imagine you want to store three instances of System.Byte into an array of Byte. This can be accomplished via the following code:

Dim anExplicitBoundArrayOfByte(2) As Byte
anExplicitBoundArrayOfByte(0) = 1
anExplicitBoundArrayOfByte(1) = 2
anExplicitBoundArrayOfByte(2) = 3


Inline Initialization with Explicit Bounds

Inline initialization is not allowed against arrays declared with explicit bounds. In such situations the only allowed syntax is the one shown in the previous code snippet.


The upper limit is enclosed in parentheses. Storing items is accomplished through indices. (The first one is always zero.) As with assignment, you can retrieve the content of a particular item using indices:

'Outputs 2
Console.WriteLine(anExplicitBoundArrayOfByte(1).ToString)

You can also perform tasks on each element in the array using a For Each loop, as in the following code snippet, which works the same on an array of Byte and on array of String:

For Each value As Byte In anExplicitBoundArrayOfByte
    Console.WriteLine(value)
Next
For Each value As String In anArrayOfThreeStrings
    Console.WriteLine(value)
Next

Another important task you should perform when working with not explicitly bound arrays is checking whether they contain something. You can accomplish this checking whether the array is Nothing:

If anArrayOfString Is Nothing Then
    'The array is not initialized
End If

This is because attempting to access a null array causes the runtime to throw an exception.

The ReDim Keyword

In some situations you need to increase the capacity of an array that you previously declared with explicit bounds. Let’s retake one of the previous arrays:

Dim anExplicitBoundArrayOfByte(2) As Byte
anExplicitBoundArrayOfByte(0) = 1
anExplicitBoundArrayOfByte(1) = 2
anExplicitBoundArrayOfByte(2) = 3

At runtime you might need to store an additional Byte, so in this case you should first increase the array’s size. To accomplish this, the Visual Basic grammar provides a special keyword named ReDim. ReDim redeclares an array of the same type with new bounds. But this keyword would also clean all the previously stored items; so what if you just need to add a new item to an existing list without clearing? Fortunately, the Visual Basic grammar provides another keyword named Preserve that is the best friend of ReDim in such situations and enables maintaining the previously stored values, preventing cleaning. The following code redeclares the preceding array without cleaning previous values:

ReDim Preserve anExplicitBoundArrayOfByte(3)

At this point, you can add a new item using the new available index:

anExplicitBoundArrayOfByte(3) = 4

Notice how you do not specify again the type of the array when using ReDim.

Multidimensional Arrays

Arrays can have multiple dimensions. Two-dimensional (also known as rectangular) and three-dimensional arrays are the most common situations. The following code declares a two-dimensional array with four values, but with no explicit dimensions specified:

Dim multiArray(,) As Integer = {{1, 2}, {3, 4}}

You can also specify dimensions as follows:

Dim multiArrayWithExplicitBounds(5, 1) As Integer

You cannot initialize arrays inline in the case of multidimensional arrays. You can then access indices as follows:

multiArrayWithExplicitBounds(1, 0) = 1
multiArrayWithExplicitBounds(2, 0) = 2
multiArrayWithExplicitBounds(1, 1) = 3


Array Literals

Visual Basic also offers a specific feature for working with both multidimensional arrays and jagged arrays (discussed next), named Array Literals. This feature enables the compiler to infer the appropriate type for arrays. Because it requires that you are familiar with the Local Type Inference, Array Literals are discussed in Chapter 20.


Jagged Arrays

Jagged arrays are arrays of arrays and are similar to multidimensional arrays. However, they differ because each item of a dimension is an array. Here you see examples of jagged arrays of Integer. To declare a jagged array, you can use the following syntax:

'A 9-entry array on the left and
'an unbound array on the right
Dim firstJaggedArray(8)() As Integer

As you can see, a jagged array declaration is characterized by a double couple of parentheses. You can also declare a jagged array that is not explicitly bound, as in the following code snippet:

Dim unboundJaggedArray()() As Integer

Although you can perform inline initializations, this coding technique could become difficult with complex arrays. Because of this, it could be more convenient declaring the array and then assigning its indices as follows:

Dim oneIntArray() As Integer = {1, 2, 3}
Dim twoIntArray() As Integer = {4, 5, 6}
unboundJaggedArray = {oneIntArray, twoIntArray}

By the way, the following initialization is perfectly legal:

Dim unboundJaggedArray()() As Integer _
    = {New Integer() {1, 2, 3}, New Integer() {4, 5, 6}}

As I explain in Chapter 20, the Array Literals feature makes inline initialization easier. You can then normally access arrays (that is, items) in a jagged array—for example, performing a For..Each loop:

'Returns 1 2 3 4 5 6
        For Each arr As Integer() In unboundJaggedArray
            For Each item As Integer In arr
                Console.WriteLine(item.ToString)
            Next
        Next

Sorting, Creating, Copying, and Inspecting Arrays with the System.Array Class

As I mentioned at the beginning of this section, all arrays derive from the System.Array class and therefore are reference types. This is an important consideration because you have to know how to manipulate them. System.Array provides several static and instance members for performing tasks on arrays. Here we discuss the most important members and method overloads; they should be self-explanatory. IntelliSense can help by showing the necessary information. First, here’s an example of an array of byte:

Dim anArrayOfByte() As Byte = New Byte() {1, 2, 3}

In this array, bounds are not explicit. Particularly at runtime, you might need to access array indices. To avoid IndexOutOfRange exceptions, though, you do need to know at least the upper bound. Just for clarification, imagine you want to perform a For..Next loop against an array. To accomplish this, you first need to know the bounds. The GetLowerBound and GetUpperBound methods enable retrieving the lower and upper bounds of an array, as shown in the following code:

'Returns 0 and 2
Console.WriteLine("Lower bound {0}, upper bound {1}",
                  anArrayOfByte.GetLowerBound(0).ToString,
                  anArrayOfByte.GetUpperBound(0).ToString)

Both methods receive the dimension of the array as an argument. This is because they can work both on one-dimensional arrays and on multidimensional arrays. A zero dimension means that you are working with a one-dimensional array or with the first dimension of a multidimensional array. Another common task is sorting arrays. There are two methods that you can use, Sort and Reverse. Sort performs an ordering of arrays in an ascending way, whereas Reverse performs the ordering in a descending way. Starting from the anArrayOfByte array, the following code reverses the order:

'Array now contains 3, 2, 1
Array.Reverse(anArrayOfByte)

To sort the array back, you can simply invoke the Sort method:

Array.Sort(anArrayOfByte)

Both methods perform ordering according to the IComparable(Of T) interface. You can also search for a particular item within an array. For this purpose, you can use two methods: IndexOf and BinarySearch. Both return the index of the specified item, but the first one just stops searching when the first occurrence is found; the second one searches through the entire array, but only if the array is sorted according to the implementation of the IComparable interface. Their usage is very straightforward:

'A conversion to Byte is required
'Both return 1
Dim position As Integer = Array.IndexOf(anArrayOfByte, CByte(2))
Dim position2 As Integer = Array.BinarySearch(anArrayOfByte, CByte(2))

Both methods receive an Object as the second argument. But we have an array of Byte. Because just writing 2 tells the compiler to recognize such a number as an Integer, we need to explicitly convert it to Byte.


Note on System.Array Methods

System.Array also provides methods that take a lambda expression as arguments. Lambdas are discussed in Chapter 20, so we do not apply them to arrays in this chapter. A quick recap is done for your convenience in the appropriate place.


Another common task with arrays is copying. Because they are reference types, assigning an array to another just copies the reference. To create a real copy of an array, you can take advantage of the shared Copy method and of the instance CopyTo method. First, you need to declare a target array. Continuing the example about the anArrayOfByte array, you could declare the new one as follows:

'Declares an array to copy to,
'with bounds equals to the source array
Dim targetArray(anArrayOfByte.GetUpperBound(0)) As Byte

To ensure the upper bound is the same as in the original array, an invocation to the GetUpperBound method is made. Next, you can copy the array:

'Copies the original array into the target,
'using the original length
Array.Copy(anArrayOfByte, targetArray, anArrayOfByte.Length)

Array.Copy needs you to pass the source array, the target array, and the total number of items you want to copy. Supposing you want to perform a complete copy of the source array, you can just pass its length. The alternative is to invoke the instance method CopyTo:

anArrayOfByte.CopyTo(targetArray, 0)

The method receives the target array as the first argument and the index where copying must begin as the second argument. A third way for copying an array is invoking the Clone method, which is inherited from System.Object. Note that Copy and CopyTo provide more granularity and control over the copy process. The last scenario creates arrays on-the-fly. You could need to perform such a task at runtime given a number of items of a specified type—for example, when you receive several strings as the user input. The System.Array class provides a shared method named CreateInstance, which creates a new instance of the System.Array class. It receives two arguments: the System.Type that the array must be of and the upper bound. For example, the following code creates a new array of String that can store three elements:

Dim runTimeArray As Array = Array.CreateInstance(GetType(String), 2)


Pay Attention to CreateInstance

You should use CreateInstance with care because you can write code that is correctly compiled but that can cause runtime errors (for example, with regard to array bounds).


Because the first argument is the representation of the System.Type you want to assign to the array, you must use the GetType keyword to retrieve information about the type. You can assign items to each index invoking the SetValue method, which is an instance method. The following line of code assigns a string to the zero index of the previous array:

runTimeArray.SetValue(CStr("Test string"), 0)

If you want to retrieve your items, simply invoke the GetValue method specifying the index:

'Returns "Test string"
Console.WriteLine(runTimeArray.GetValue(0))

Common Operators

When working with data types, you often need to perform several tasks on them. Depending on which type you work with, the Visual Basic programming language offers different kinds of operators, such as arithmetic operators, logical and bitwise operators, and shift operators. In this section you learn about Visual Basic operators and how you can use them in your own code. Let’s begin by discussing arithmetic operators, which are probably the operators you will use most frequently.

Arithmetic Operators

Visual Basic 2012 provides some arithmetic operators, listed in Table 4.11.

Table 4.11. Arithmetic Operators

Image

The first three operators are self-explanatory, so I would like to focus on the other ones. First, an important consideration should be done on the division operators. As shown in Table 4.11, Visual Basic offers two symbols, the slash (/) and backslash (). The first one can be used in divisions between floating-point numbers (such as Double and Single types), and the second can be used only in divisions between integer numbers. This backslash is fast when working with integers and truncates the result in case it is a floating-point number. The backslash accepts and returns just integers. To understand this concept, consider the following division between Doubles:

'Division between double: returns 2.5
Dim dblResult As Double = 10 / 4

The result of such calculation is 2.5. Now consider the following one:

'Division between integers: returns 2
Dim intResult As Integer = 10 4

The result of this calculation is 2. This is because the operator truncated the result, due to its integer nature. If you try to use such operators in a division involving floating-point numbers, the Visual Basic compiler throws an exception, which is useful for avoiding subtle errors. By the way, such an exception occurs only with Option Strict On, which you should always set as your default choice.


Supported Types

The integer division operator supports the SByte, Byte, Short, UShort, Integer, UInteger, Long, and ULong data types, which are all numeric types that do not support a floating point.


For divisions between floating-point numbers, it’s worth mentioning that divisions between Single and Double are also allowed but cause the compiler to perform some implicit conversions that should be avoided. In such situations, you should just perform an explicit conversion, as in the following code:

'Division between Single and Double
Dim singleValue As Single = 987.654
Dim doubleValue As Double = 654.321
Dim division As Single = singleValue / CSng(doubleValue)

The next interesting operator is the exponentiation operator. A simple example follows:

Dim result As Double = 2 ^ 4  'returns 16

The exponentiation operator returns a Double value. Because of this, even if operands are other types (such as Integer or Long), they will be always converted to Double. Behind the scenes, the ^ operator invokes the Pow method exposed by the System.Math class. So you could also rewrite the preceding line of code as follows:

Dim result As Double = System.Math.Pow(2,4)  'returns 16

The last built-in operator is Mod (which stands for modulus) that returns the remainder of a division between numbers. The following lines of code show an example:

'Mod: returns 0
Dim remainder As Integer = 10 Mod 2
'Mod: returns 1
Dim remainder As Integer = 9 Mod 2

A typical usage of Mod is for determining whether a number is an odd number. To accomplish this, you could create a function like the following:

Function IsOdd(ByVal number As Integer) As Boolean
  Return (number Mod 2) <> 0
End Function

If the remainder is different from zero, the number is odd and therefore returns True. Mod supports all numeric types, including unsigned types and floating-point ones. The .NET Framework offers another way for retrieving the remainder of a division, which is the System.Math.IEEERemainnder method that works as follows:

'Double remainder
Dim dblRemainder As Double = System.Math.IEEERemainder(10.42, 5.12)

Although both Mod and IEEERemainder return the remainder of a division between numbers, they use different formulas behind the scenes. Thus, the result can differ. According to the MSDN documentation, this is the formula for the IEEERemainder method:

IEEERemainder = dividend - (divisor * Math.Round(dividend / divisor))

This is instead the formula for the Modulus operator:

Modulus = (Math.Abs(dividend) - (Math.Abs(divisor) *
          (Math.Floor(Math.Abs(dividend) / Math.Abs(divisor))))) *
          Math.Sign(dividend)

You can see how calculations work differently, especially where Modulus gets the absolute value for dividend and divisor.


System.Math Class

This section provides an overview of the arithmetic operators built in to the Visual Basic 2012 programming language. The System.Math class provides lots of additional methods for performing complex calculations, but this is beyond the scope here.


Assignment Operators

You can use operators shown in the previous paragraph for incremental operations. Consider the following code:

Dim value As Double = 1

value += 1 'Same as value = value + 1

value -= 1 'Same as value = value - 1

value *= 2 'Same as value = value * 2

value /= 2 'Same as value = value / 2

value ^= 2 'Same as value = value ^ 2

Dim test As String = "This is"
test &= " a string" 'same as test = test & " a string"

You can abbreviate your code using this particular form when performing operations or concatenations. Also notice that += assignment operator works on strings as well.

Logical, Bitwise, and Shift Operators

Visual Basic 2012 offers logical, bitwise, and shift operators. Logical operators are special operators enabling comparisons between Boolean values and returning Boolean values. Bitwise and shift operators enable performing operations bit by bit. Next let’s discuss both logical and bitwise operators.

Logical Operators

Visual Basic 2012 has eight logical/bitwise operators: Not, And, Or, Xor, AndAlso, OrElse, IsFalse, and IsTrue. In this section you learn about the first four; the other ones are covered in the next section. The first operator, Not, returns the opposite of the actual Boolean value. For example, the following lines of code return False because, although the 43 number is greater than 10, Not returns the opposite:

'Returns False
Dim result As Boolean = (Not 43 > 10)

Logical operators can also be used with reference types. For example, you can return the opposite of the result of a comparison between objects (see the section “Comparison Operators” for details):

Dim firstPerson As New Person
Dim secondPerson As New Person
'Returns True
result = (Not firstPerson Is secondPerson)

This code returns True; the comparison between firstPerson and secondPerson returns False because they point to two different instances of the Person class, but Not returns the opposite. The next operator is And, which compares two Boolean values or expressions and returns True if both values or expressions are True; otherwise, if at least one value is False, And returns False. Here is an example of And:

'Returns False
result = 10 > 15 and 30 > 15
'Returns True
result = 20 > 15 and 30 > 15
'Returns True
result = 20 > 15 and 15 = 15

And is also useful for comparing Boolean properties of objects. For example, you might want to check whether a text file exists on disk and that it is not zero-byte; you could write the following code:

If My.Computer.FileSystem.FileExists("C:MyFile.txt") = True And
    My.Computer.FileSystem.ReadAllText("C:MyFile.txt").Length > 0 Then
       'Valid file
End If

If both actions return True, And returns True. In our example this should mean that we encountered a valid text file. The next operator is Or. Such an operator works like this: if expressions or values are True, it returns True; if both are False, it returns False; and if one of the two expressions is True, it returns True. The following code demonstrates this scenario:

'Returns True
result = 10 > 15 or 30 > 15
'Returns True
result = 10 < 15 or 30 > 15
'Returns False
result = 10 > 15 or 30 < 15

The last operator is Xor (eXclusive or). Such an operator compares two Boolean expressions (or values) and returns True only if one of the two expressions is True; in all other cases it returns False. Continuing the first example, Xor returns the values described inside comments:

'Returns True
result = 10 > 15 Xor 30 > 15
'Returns False
result = 20 > 15 Xor 30 > 15
'Returns False
result = 20 > 15 Xor 15 = 15

Short-Circuiting Operators

Sometimes you do not need to perform the evaluation of the second expression in a Boolean comparison because evaluating the first one provides the result you need. In such scenarios, you can use two short-circuiting operators, AndAlso and OrElse. Short-circuiting means that code execution is shorter and performances are improved. Such operators are particularly useful when you need to invoke an external method from within an If..Then code block. Let’s consider again the previous example for the And operator:

If My.Computer.FileSystem.FileExists("C:MyFile.txt") = True And
    My.Computer.FileSystem.ReadAllText("C:MyFile.txt").Length > 0 Then
       'Valid file
End If

The Visual Basic compiler performs both evaluations. What would happen if the file did not exist? It throws a FileNotFoundException when the ReadAllText method is invoked because the And operator requires both expressions to be evaluated. You should implement error-handling routines for such code, but this example is just related to operators. You can prevent your code from encountering the previously described problem using AndAlso. You need to replace And with AndAlso, as in the following code:

    If My.Computer.FileSystem.FileExists("C:MyFile.txt") = True AndAlso
My.Computer.FileSystem.ReadAllText("C:MyFile.txt").Length > 0 Then
        'Valid file
    End If

AndAlso evaluates the first expression; if this returns False, the second expression is not evaluated at all. In this case, if the file does not exist, the code exits from the If block. AndAlso’s counterpart is OrElse, which evaluates the second expression only when the first one is False. Finally, in Visual Basic are two other operators named IsTrue and IsFalse. The first one works in conjunction with the OrElse operator, while the second works with the AndAlso. You cannot explicitly invoke these operators in your code because it is the job of the Visual Basic compiler to invoke them within an evaluation expression. This means that the types you want to be evaluated via OrElse or AndAlso must expose both of them. The following is a simple sample:

Public Structure myType
    Public Shared Operator IsFalse(ByVal value As myType) As Boolean
        Dim result As Boolean
        ' Insert code to calculate IsFalse of value.
        Return result
    End Operator
    Public Shared Operator IsTrue(ByVal value As myType) As Boolean
        Dim result As Boolean
        ' Insert code to calculate IsTrue of value.
        Return result
    End Operator
End Structure

Bitwise Operators

Performing bitwise operations means performing operations with two binary numbers, bit by bit. The problem here is that Visual Basic does not allow working directly with binary numbers, so you need to write code against decimal or hexadecimal numbers that the Visual Basic compiler will actually treat, behind the scenes, in their binary representation. However, you still need to write them in a comprehensible way.


Converting Between Decimal and Binary

You can use the Windows Calculator in scientific mode to perform conversions between decimal/hexadecimal and binary numbers.


The bitwise operators in Visual Basic are still And, Or, Not, and Xor. Unlike logical operations, though, in which such operators evaluate expressions, bitwise operations are related to bit manipulations. You might wonder why you would need to perform bitwise operations in the era of WPF, Silverlight, and other high-level technologies. You could get multiple answers to this question, but the most useful one is probably providing the example of applications that interact with hardware devices in which you still need to work in a bit-by-bit fashion. Another common situation in Visual Basic is the combination of Enum flags. Let’s see some examples. The And operator combines two operands into a result. Inside such a result, it puts a 1 value where both operands have 1 in a particular position; otherwise, it puts a zero. For a better explanation, consider the following code:

Dim result As Integer = 152 And 312

The binary counterpart for 152 is 10011000, whereas the binary counterpart for 312 is 100111000. The result variable’s value is 24, whose binary counterpart is 11000. If you observe the following representation

 10011000
100111000
    11000

You can notice how the third line, which represents the result of the And operation, contains 1 only in positions in which both operands have 1. If you then convert the result back to a decimal number, you get 24. The Or operator works similarly: It combines two operands into a result; inside such a result, it puts a 1 value if at least one of the operands has a 1 value in a particular position. Consider this code:

Dim result As Integer = 152 Or 312

Both 152 and 312 binary counterparts are the same as the previous example. The Or operator produces 110111000 as a binary output, whose decimal counterpart is 440. To understand this step, take a look at this comparison:

 10011000
100111000
110111000

It’s easy to see that the result contains 1 where at least one of the operands contains 1 in a particular position. The Xor operator combines two operands into a result; inside such a result, it puts a 1 value if at least one of the operands has a 1 value in a particular position, but not if both have 1 in that position. (In such a case it places 0.) Consider this bitwise operation:

Dim result As Integer = 152 Xor 312

The 152 and 312 binary counterparts are the same as in the preceding example. But this line of code returns 416, whose binary counterpart is 110100000. So let’s see what happened:

 10011000
100111000
110100000

As you can see, Xor placed 1 where at least one of the operands has 1 in a particular position, but where both operands have 1, it placed 0. The Not operator is probably the easiest to understand. It just reverses the bits of an operand into a result value. For example, consider this line of code:

Dim result As Integer = Not 312

In the following comparison, the second line is the result of the preceding negation:

100111000
011000111

This result has −313 as its decimal counterpart.


Binary Numbers

This book does not teach binary numbers, so the code shown in this and in the following section assumes that you are already familiar with binary representations of decimal numbers.


Shift Operators

Shift operators are also something that makes more sense with binary numbers than with decimal or hexadecimal numbers, although you need to provide them via their decimal representations. With shift operators, you can move (that is, shift) a binary representation left or right for the specified number of positions. The left-shift operator is <<, and the right-shift operator is >>. For example, consider the following Integer:

'Binary counterpart is
'101000100
Dim firstValue As Integer = 324

The binary representation for 324 is 101000100. At this point, we want to left-shift the binary for four positions. The following line accomplishes this:

'Returns 5184, which is
'1010001000000
Dim leftValue As Integer = firstValue << 4

With the left-shifting of four positions, the number 101000100 produces 1010001000000 as a result. Such binary representation is the equivalent of the 5184 decimal number, which is the actual value of the leftValue variable. The right-shift operator works the same but moves positions on the right:

'Returns 20, which is
'10100
Dim rightValue As Integer = firstValue >> 4

This code moves 101000100 for four positions to the right, so the binary result is 10100. Its decimal equivalent is then 20, which is the actual value of the rightValue variable.


Supported Types

Shift operators support Byte, Short, Integer, Long, SByte, UShort, UInteger and ULong data types. When using shift operators with unsigned types, there is no sign bit to propagate; therefore, the vacated positions are set to zero.


Concatenation Operators

As in the previous versions, Visual Basic 2012 still offers concatenation operators—the + and & symbols. The main difference is that the + symbol is intended for numeric additions, although it can also work with strings; the & symbol is defined only for strings, and should be preferred when concatenating strings so that you can avoid possible errors. Listing 4.2 shows an example of concatenation.

Listing 4.2. Concatenation Operators


Module ConcatenationOperators
    Sub ConcatenationDemo()

        Dim firstString As String = "Alessandro"
        Dim secondString As String = "Del Sole"

        Dim completeString As String = firstString & secondString

        'The following still works but should be avoided
        'Dim completeString As String = firstString + secondString

    End Sub
End Module


Comparison Operators

As in its predecessors, Visual Basic 2012 still defines some comparison operators. Typically, comparison operators are of three kinds: numeric operators, string operators, and object operators. Let’s see these operators in detail.

Numeric Comparison Operators

You can compare numeric values using the operators listed in Table 4.12.

Table 4.12. Numeric Comparison Operators

Image

These operators return a Boolean value that is True or False. The following code snippet shows an example (comments within the code contain the Boolean value returned):

Sub NumericOperators()

    Dim firstNumber As Double = 3
    Dim secondNumber As Double = 4
    Dim comparisonResult As Boolean = False

    'False
    comparisonResult = (firstNumber = secondNumber)
    'True
    comparisonResult = (secondNumber > firstNumber)
    'False
    comparisonResult = (secondNumber <= firstNumber)
    'True
    comparisonResult = (secondNumber <> firstNumber)
End Sub

String Comparison Operators

String comparison was discusses in the section “Working with Strings,” so refer to that topic.

Objects Comparison Operators: Is, IsNot, and TypeOf

You can compare two or more objects to understand whether they point to the same instance or what type of object you are working with. The three operators for comparing objects are Is, IsNot, and TypeOf. Is and IsNot are used to understand whether two objects point to the same instance. Consider the following code:

Dim firstPerson As New Person
Dim secondPerson As New Person

'Returns True, not same instance
If firstPerson IsNot secondPerson Then

End If

'Returns False, not same instance
If firstPerson Is secondPerson Then

End If

'Returns True, same instance
Dim onePerson As Person = secondPerson
If secondPerson Is onePerson Then

End If

firstPerson and secondPerson are two different instances of the Person class. In the first comparison, IsNot returns True because they are two different instances. In the second comparison, Is returns False because they are still two different instances. In the third comparison, the result is True because you might remember that simply assigning a reference type just copies the reference to an object. In this case, both secondPerson and onePerson point to the same instance. The last example is related to the TypeOf operator. Typically, you use it to understand whether a particular object has inheritance relationships with another one. Consider the following code snippet:

'Returns True
Dim anotherPerson As Object = New Person
If TypeOf anotherPerson Is Person Then

End If

We have here an anotherPerson object of type Object, assigned with a new instance of the Person class. (This is possible because Object can be assigned with any .NET type.) The TypeOf comparison returns True because anotherPerson is effectively an instance of Person (and not simply object). TypeOf is useful if you need to check for the data type of a Windows Forms or WPF control. For example, a System.Windows.Controls.Button control in WPF inherits from System.Windows.Controls.FrameworkElement, and then TypeOf x is FrameworkElement and returns True.


Operators Precedence Order

Visual Basic operators have a precedence order. For further information, refer to the MSDN Library: http://msdn.microsoft.com/en-us/library/fw84t893(v=vs.110).aspx.


Iterations, Loops, and Conditional Code Blocks

Hundreds of programming techniques are based on loops and iterations. Both loops and iterations enable the repetition of some actions for a specific number of times or when a particular condition is True or False. All these cases are discussed next.

Iterations

Iterations in Visual Basic 2012 are performed via the For..Next and For Each loops. Let’s analyze them more in details.

For..Next

A For..Next loop enables repeating the same action (or group of actions) for a finite number of times. The following code shows an example in which the same action (writing to the Console window) is performed 10 times:

For i As Integer = 1 To 10
    Console.WriteLine("This action has been repeated {0} times", i)
Next

In such loops, you need to define a variable of a numeric type (i in the preceding example) that acts as a counter.


Tip

You can also assign the variable with another variable of the same type instead of assigning a numeric value.


The previous code produces the following result:

This action has been repeated 1 times
This action has been repeated 2 times
This action has been repeated 3 times
This action has been repeated 4 times
This action has been repeated 5 times
This action has been repeated 6 times
This action has been repeated 7 times
This action has been repeated 8 times
This action has been repeated 9 times
This action has been repeated 10 times

Notice that you can also initialize the counter with zero or with any other numeric value.


Tip

You can use the Integer or UInteger variables as counters in For..Next loops. This is because these data types are optimized for the Visual Basic compiler. Other numeric types are also supported but are not optimized, so you are encouraged to always use Integer or UInteger.


You can also decide how the counter must be incremented. For example, you could decide to increment the counter of two units instead of one (as in the previous example). This can be accomplished via the Step keyword:

For i As Integer = 1 To 10 Step 2
    Console.WriteLine("Current value is {0}", i)
Next

This code produces the following output:

Current value is 1
Current value is 3
Current value is 5
Current value is 7
Current value is 9

Step can also work with negative numbers and lets you perform a going-back loop:

For i As Integer = 10 To 1 Step -2
    Console.WriteLine("Current value is {0}", i)
Next

You can also decide to break a For loop when a particular condition is satisfied and you do not need to still perform the iteration. This can be accomplished with the Exit For statement, as shown in the following example:

For i As Integer = 1 To 10
    Console.WriteLine("Current value is {0}", i)
    If i = 4 Then Exit For
Next

In the preceding example, when the counter reaches the value of 4, the For loop is interrupted and control is returned to the code that immediately follows the Next keyword. There is also another way for controlling a For loop, such as when you need to pass the control directly to the next iteration of the loop when a particular condition is satisfied (which is the opposite of Exit For). This can be accomplished with the Continue For statement, as shown in the following code snippet:

For i As Integer = 1 To 10
    If i = 4 Then  'Ignore the 4 value
        i += 1 'Increments to 5
        Continue For  'Continues from next value, that is 6
    End If
    Console.WriteLine("Current value is  {0}", i)
Next

In the preceding example we are doing some edits on the counter. Notice that each time you invoke a Continue For, the counter itself is incremented one unit.

For Each

A For Each loop allows performing an action or a group of actions on each item from an array or a collection. Although collections are discussed in Chapter 16, I’m providing a code example with them because this is the typical usage of a For Each loop. For example, consider the following code:

'A collection of Process objects
Dim procList As List(Of Process) = Process.GetProcesses.ToList

For Each proc As Process In procList
    Console.WriteLine(proc.ProcessName)
    Console.WriteLine("     " & proc.Id)
Next

In the preceding code snippet is a collection containing references to all the running processes on the machine. Each process is represented by an instance of the System.Diagnostics.Process class; therefore, List(Of Process) is a collection of processes. Supposing we want to retrieve some information for each process, such as the name and the identification number, we can iterate the collection using a For Each statement. You need to specify a variable (also known as control variable) that is the same type of the item you are investigating. In the preceding code you are just performing reading operations, but you can also edit items’ properties. For example, you might have a collection of Person objects and could retrieve and edit information for each Person in the collection, as in the following code:

'A collection of Person objects
Dim people As New List(Of Person)
'Populate the collection here..
'....
For Each p As Person In people
    p.LastName = "Dr. " & p.LastName
    Console.WriteLine(p.LastName)
Next

This code will add the Dr. prefix to the LastName property of each Person instance.


For Each Availability

Behind the scenes, For Each can be used against objects that implement the IEnumerable or IEnumerable(Of T) interfaces. Such objects expose the enumerator that provides support for For Each iterations.


You can still use Exit For when you need to break out from a For Each statement. A For Each loop has better performances with collections than with arrays, but you can use it in both scenarios.


Iterators

Visual Basic 2012 introduces a new feature, the iterators. With iterators, you can return elements from the collection or the array to the caller code while the iteration is still in progress, so that such elements can be used by the caller without having to wait for the iteration to be completed. Iterators are typically used with collections, so this feature is discussed in Chapter 16.


Visual Basic 2012 Compiler Fixes

Until Visual Basic 2010, if you wrote a For..Each loop to iterate the result of a lambda expression or the content of a variable of type IQueryable or IQueryable(Of T), you had to declare a temporary variable to avoid the following error message:

Using the iteration variable in a query might have unexpected results

The following code shows an example:

Private Function SearchProducts(ParamArray keywords As String()) As _
        IQueryable(Of Product)
  Dim query As IQueryable(Of Product) = dataContext.Products

  For Each keyword As String In keywords

    query = query.Where(Function(p) p.Description.Contains(keyword))

  Next
  Return query
End Function

As you can see, a lambda expression exists inside the loop, whose result is assigned to the query variable. Such a lambda is using the control variable (keyword) directly, and this was not allowed because only one slot of memory was allocated and every lambda expression or query was referring to the same variable. Because of this, until Visual Basic 2012 you had to rewrite the previous code as follows:

Private Function SearchProducts(ParamArray keywords As String()) As _
        IQueryable(Of Product)
  Dim query As IQueryable(Of Product) = dataContext.Products

  For Each keyword As String In keywords

    Dim temp As String = keyword
    query = query.Where(Function(p) p.Description.Contains(temp))

  Next
  Return query
End Function

By declaring a temporary variable (temp in the example), you could solve the problem. The Visual Basic 2012 compiler solves the memory allocation problem and makes it possible to use the control variable directly, without the need of declaring an intermediate variable. So, in Visual Basic 2012 you could write the previous example as in the first code snippet.

Loops

As in the previous versions of the language, Visual Basic 2012 offers two kinds of loops: Do..Loop and While..End While. In this section we take a look at both loops.

Do..Loop

The Do..Loop is the most frequently used loop in Visual Basic and the most flexible. This loop can have two behaviors: repeating a set of actions until a condition is false and repeating a set of actions until a condition is true. The first scenario is accomplished using a Do While statement, as demonstrated in Listing 4.3.

Listing 4.3. Performing a Do While Loop


Sub LoopWhileDemo()
    Dim max As Integer = 0
    Do While max < Integer.MaxValue
        max += 1
        'Do something else here
        If max = 7000000 Then Exit Do
    Loop
    Console.WriteLine("Done: " & max.ToString)
End Sub


The code is quite easy: Whereas the value of max is less than the maximum value of the Integer type, increment max itself is one unit. Do While evaluates a False condition. (The loop goes on because max is less than Integer.MaxValue.) The code also demonstrates how you can exit from a loop using an Exit Do statement. This passes the control to the next statement after the Loop keyword. The other scenario is when you need to evaluate a True condition. This can be accomplished via a Do Until loop. Listing 4.4 demonstrates this.

Listing 4.4. Demonstrating a Do Until Loop


Sub LoopUntilDemo()
    Dim max As Integer = 0
    Do Until max = Integer.MaxValue
        max += 1

        If max = 7000000 Then Exit Do
    Loop
    Console.WriteLine("Done: " & max.ToString)
End Sub


The difference here is that the loop ends when the condition is True—that is, when the value of max equals the value of Integer.MaxValue. As before, Exit Do can end the loop. The interesting thing in both cases is that you can evaluate the condition on the Loop side instead of the Do one. Listing 4.5 shows how you could rewrite both examples.

Listing 4.5. Evaluating Conditions on the Loop Line


'Loop is executed at least once
Sub LoopUntilBottomDemo()
    Dim max As Integer = 0
    Do
        max += 1
        If max = 7000000 Then Exit Do
    Loop Until max = Integer.MaxValue
    Console.WriteLine("Done: " & max.ToString)
End Sub

'Loop is executed at least once
Sub LoopWhileBottomDemo()
    Dim max As Integer = 0
    Do
        max += 1
        If max = 7000000 Then Exit Do
    Loop While max < Integer.MaxValue
    Console.WriteLine("Done: " & max.ToString)
End Sub


Both loops behave the same way as previous ones, with one important difference: Here the loop is executed at least once.


Performance Tips

The For loops are faster than Do ones. Because of this, you should use For loops, particularly when you know that you will do a finite number of iterations.


While..End While

A While..End While loop performs actions when a condition is False. Listing 4.6 shows an example.

Listing 4.6. While..End While Loop


Sub WhileEndWhileDemo()
    Dim max As Integer = 0
    While max < Integer.MaxValue
        max += 1

        If max = 7000000 Then Exit While
    End While
    Console.WriteLine("Done: " & max.ToString)
End Sub


The loop behaves the same as Do While because both evaluate the same condition.


Note

The While..End While loop is less efficient than a Do While and is deprecated, although it is still supported. You should therefore always use a Do While loop.


Conditional Code Blocks

If..Then..Else

The If..Then..Else is the most classical block for conditionally executing actions. An If evaluates an expression as True or False and, according to this, allows specifying actions to take place. Listing 4.7 shows an example.

Listing 4.7. Demonstrating the If..Then..Else Block


Sub IfThenElseDemo()
    Console.WriteLine("Type a number")
    'Assumes users type a valid number
    Dim number As Double = CDbl(Console.ReadLine)

    If number >= 100 Then
        Console.WriteLine("Your number is greater than 100")
    ElseIf number < 100 And number > 50 Then
        Console.WriteLine("Your number is less than 100 and greater than 50")
    Else
        'General action
        Console.WriteLine("Your number is: {0}", number)
    End If
End Sub


If checks whether the condition is True; if so, it takes the specified action. You can also specify to evaluate a condition for False (for example, If something = False Then). You can also use an ElseIf to delimit the condition evaluation. If no expression satisfies the condition, the Else statement provides an action that will be executed in such a situation.


Coding Tip

The Visual Studio IDE offers a useful feature known as code blocks delimiters selection. Because you can nest different If..Then blocks or can have a long code file, when you place the cursor near either the If/Then keywords or the End If statement, the IDE highlights the related delimiter (End If, if you place the cursor on an If, and vice versa).


Notice how the code uses an And operator to evaluate the condition. You can use other operators such as logical and short-circuit operators as well. Another typical example is when you need to check whether a condition is false using the Not operator. The following is an example:

If Not number >= 100 Then
    'Number is False
End If

Not also requires the same syntax when working with reference types, but in this case you can also use the IsNot operator. The following example checks whether the instance of the Person class is not null:

Dim p As Person  'p is actually null

'You can check with IsNot
If p IsNot Nothing Then
    'p is not null
Else
    'p is null
End If

IsNot is not available with value types.

The IIf Operator

The IIf operator evaluates an expression and returns one of two objects, according to the evaluation result which can be true or false. The syntax for the IIf operator is the following:

Dim result As Object = IIf(Expression As Boolean, TruePart As Object,
                       FalsePart As Object)

So the first argument of the operator is a Boolean expression. If the expression result is True, the operator returns the second argument (TruePart) otherwise it returns the third one (FalsePart). The following code demonstrates how to evaluate if the user input is null and show a text message according to the result:

Dim input As String = Console.ReadLine

Dim result As Object = IIf(input Is Nothing, "Enter something please",
                           String.Concat("You entered ", input))

Console.WriteLine(CStr(input))

Remember that the IIf operator and its arguments must return Object, so you will have to make the appropriate type conversions when needed.

Select Case

Select Case is a statement that allows evaluating an expression against a series of values. Generally, Select Case is used to check whether an expression matches a particular value in situations evaluated as True. Listing 4.8 provides an example.

Listing 4.8. Using the Select Case Statement for Evaluating Expressions


Sub SelectCaseDemo()
    Console.WriteLine("Type a file extension (without dot):")
    Dim fileExtension As String = Console.ReadLine

    Select Case fileExtension.ToLower
        Case Is = "txt"
            Console.WriteLine("Is a text file")
        Case Is = "exe"
            Console.WriteLine("Is an executable")
        Case Is = "doc"
            Console.WriteLine("Is a Microsoft Word document")
        Case Else
            Console.WriteLine("Is something else")
    End Select
End Sub


The code in Listing 4.8 simply compares the string provided by the user with a series of values. If no value matches the string, a Case Else is used to provide a general result. Comparison is performed with the Is operator and the equality operator. The following syntax is also accepted:

Case "txt"

IntelliSense adds the Is = symbology by default. You can also break from a Select Case statement in any moment using an Exit Select statement. Select..Case also offers another syntax to apply when you want to check whether a value falls within a particular range. To accomplish this, you use the To keyword instead of the Is = operators, as in the following code that waits for the user to enter a number and then checks what range the number falls in:

Console.WriteLine("Enter a number from 1 to 50:")
Dim result As Integer = CInt(Console.ReadLine)
Select Case result
    'The user entered a number in the range from 1 to 25
    Case 1 To 25
        Console.WriteLine("You entered {0} which is a small number",
                          result.ToString)
        'The user entered a number in the range from 26 to 50
    Case 26 To 50
        Console.WriteLine("You entered {0} which is a high number",
                          result.ToString)
        'The user entered a number < 1 or > 50
    Case Else
        Console.WriteLine("You entered a number which is out of range")
End Select

In other words, considering the preceding example, Case 1 To 25 means in case the value to check is in the range between the left value (1) and the right value (25), then take the nested action. You can also check for multiple items not ranged with the following syntax:

Select Case result
    'The user entered 1 or a value between 5 and 10 or 12
    'All other values are excluded
    Case Is = 1, 5 To 10, 12
        '...
End Select


Coding Tip

For the If..End If block, the code blocks delimiters selection feature is also available for Select..End Select blocks.



Performance Tips

The Visual Basic compiler evaluates expressions as a sequence. Because of this, in Select Case statements it evaluates all conditions until the one matching the value is found. Consequently, the first Case instructions in the sequence should be related to values considered the most frequent.


Constants

Constants provide a way for representing an immutable value with an identifier. There could be situations in which your applications need to use the same value (which can be of any .NET type); therefore, it can be convenient to define an easy-to-remember identifier instead of a value. What would happen if such a value were a Long number? You declare constants as follows:

Const defaultIntegerValue As Integer = 123456789
Const aConstantString As String = "Same value along the application"

Constants are read-only fields that can be declared only at the module and class level or within a method and must be assigned with a value when declared. Constants within methods have public visibility by default, whereas constants at the module and class level can have one of the .NET scopes, as in the following lines:

Private Const defaultIntegerValue As Integer = 123456789
Public Const aConstantString As String= "Same value along the application"

The reason constants must be assigned when declared is that the expression is evaluated at compile time. Starting from Visual Basic 2008, there are a couple of things to consider. Look at the following line of code:

Private Const Test = "Test message"

The type for the Test variable is not specified. Until Visual Basic 2005, with Option Strict Off, such a declaration would assign Object. In later versions of the language, if Option Infer is On, the compiler assigns String; if it is Off, the compiler goes back to assigning Object.

With..End With Statement

Visual Basic provides an alternative way for invoking object members—the With..End With statement. Consider the following code block, in which a new Person class is instantiated and then properties are assigned while methods are invoked:

Dim p As New People.Person
p.FirstName = "Alessandro"
p.LastName = "Del Sole"
Dim fullName As String = p.ToString

Using a With..End With statement, you just need to specify the name of the class once and then type a dot so that IntelliSense shows members you can use, as follows:

Dim p As New People.Person
With p
    .FirstName = "Alessandro"
    .LastName = "Del Sole"
    Dim fullName As String = .ToString
End With

There is no difference between the two coding techniques, so feel free to use the one you like most. With..End With offers the advantage of speeding up the code writing a little and can be useful if you have a lot of members to invoke or assign at one time.


With..End With

With..End With has no equivalent in other .NET languages, so if you have to interoperate, assigning members the normal way can be a good idea. Although the compiler translates the With..End With blocks as single members’ invocations, in such scenarios the best approach is a .NET-oriented coding style instead of a VB-oriented one.


Summary

Every development environment relies on data types. The .NET Framework relies on two kinds of data types: value types and reference types. Both kinds of types are managed by the Common Type System, which provides a common infrastructure to .NET languages for working with types. In this chapter, you learned the important basics of the .NET development and the Visual Basic language, which can be summarized as follows:

• Common Type System

• Value types and reference types

System.Object and inheritance levels in value types and reference types

• Memory allocation of both value types and reference types

• Converting between types and conversion operators

• Most common value types and reference types

• Common operators

You often need to work with and analyze data types. Visual Basic 2012 provides several ways for performing work on types and the data they store. To accomplish this, you can use

• Iterations, such as For..Next and For..Each

• Loops, such as Do..Loop

• Conditional code blocks, such as If..End If and Select Case..End Select

It’s important to understand all the preceding features because they often recur in your developer life; these features appear extensively in the rest of the book. But you also might encounter errors when working with types. The next two chapters discuss two fundamental topics in the .NET development with Visual Basic: debugging and handling errors.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.21.175