Chapter 2. Understanding Data Types

In This Chapter

  • Declaring variables

  • Seeing the differences between value types and reference types

  • Understanding how data types are used in the .NET Framework

  • Handling more than one variable

  • Finding data types and using them in your code

The data type you specify for the variable declaration determines everything about how that variable is treated throughout its entire life cycle. This chapter shows the data types available in the .NET Framework and how to create your own data types.

The Rules of Data Types

Data types are the fundamental organizing blocks of code in .NET. Your program can do very little without using data types. If you've never programmed with .NET, you might be thinking of data types in terms of integers and characters. Those are one kind of data type, but other data types are more flexible, sophisticated, and powerful than integers and characters. Examples include classes, structures, and enumerations.

Variables are used to access data that you place in your computer's memory. When you declare a variable, you specify its data type. This is how your computer program knows how to assign meaning to the ones and zeroes that you store in your computer's memory.

From the perspective of the computer, all data is just a set of ones and zeroes. Computer programs apply meaning to those ones and zeroes by using data types. A data type tells your computer how your program can interact with the data and what operations are legal.

Examples of data types are integers and characters. By telling your computer program that a given variable holds an integer data type, the computer knows that you can add and subtract the value. The same data type law tells your computer that you can't add and subtract characters. When you use data types, you tell the computer the rules for interacting with your data.

These rules are to your benefit. The compiler for your program uses the rules for your data type to tell you in advance whether your program will break when you run it. Data types allow the compiler and Visual Studio to give you feedback about your code before you even execute it. Giving you feedback early in the process allows you to correct errors before your end users find them.

The many kinds of data types include simple data types (such as integers and characters) as well as complex data types (such as those provided by the .NET Framework). You can create your own data types by combining simple and complex data types.

Making a Declaration

Any time you want to use data in your program, you must first declare a variable to hold the value in memory. When you declare a variable, you do two things:

  • Name your variable. You create an identifier that allows you to access the variable's value in memory and pass it around in your program.

  • Specify a data type. You tell the computer how to allocate memory for the variable.

Note

You'll most frequently declare variables in your programs, but you aren't limited to declaring only variables. You can declare constants, enumerations, functions, namespaces, and new data types.

Not all programming languages require you to specify a data type when you declare a variable. Languages (such as the languages of the .NET Framework) that require you to specify a data type are strongly typed languages. Specifying a data type at the time of variable declaration

  • Enables the Code Editor to use features, such as IntelliSense

  • Allows the compiler to provide you feedback if you try to use data types improperly

Declaring a variable is a straightforward process. For example, here's how you declare a variable in Visual Basic and C#:

VB:

Dim myVariable as DataType

C#:

DataType myVariable;

After you declare a variable, you might assign a value to it. The first value assigned to a variable is its initial value. Assigning a value to a variable for the first time is initializing the variable.

Note

You don't have to initialize variables. The .NET Framework provides default initial values for you, depending on the data type; however, the initial values might not be what you expect. Certain kinds of data types are initialized to null, for example. You can't work with null in your program. You want to make sure that you assign a value to your variables before you start taking actions on those variables in your program.

You can initialize a variable when you declare it. For example, the following statement declares a variable of type integer and sets the variable's initial value to 7.

VB:

Dim i As Integer = 7

C#:

int i = 7;

Alternatively, you can assign an initial or subsequent value by using the equal (=) operator, as shown in the following snippets:

VB:

i = 6

C#:

i = 6;

Complex data types called classes require you to use the new operator when you assign a value. For example, the following code declares a variable of the data type System.Data.DataSet. The second line of code uses the new operator to assign a value to the variable.

VB:

Dim ds as System.Data.DataSet
ds = new System.Data.DataSet

C#:

System.Data.DataSet ds;
ds = new System.Data.DataSet();

Using the new operator creates an instance of the class. The class is like a template that defines what values can be stored. When you assign an instance of a class to a variable, the variable is like a blank entry form based on the class template.

You can declare and initialize a class in one line:

Dim ds as new System.Data.DataSet

The .NET Framework's Common Type System

One of the services provided by the .NET Framework is data type management in the form of the Common Type System (CTS). The CTS defines all the rules for how the programming language you use

  • Declares types

  • Creates new types

  • Uses types in your source code

The CTS ensures data type consistency among the programming languages of the .NET Framework.

Understanding the type hierarchy

The CTS has a type hierarchy that provides a base set of data types used by all the .NET programming languages. The root data type for all data types in the .NET Framework is System.Object.

The most common use of data types is declaring variables, as shown in the preceding section. When you initialize a variable, either through assignment or the new operator, the computer uses the variable's data type to know how to allocate the memory for the variable. You can use two basic kinds of data types in .NET:

  • Value types: Simple data types that are built into most programming languages, such as integer and Boolean.

    Tip

    The .NET Framework has a set of built-in value types that derive from the data type System.ValueType.

  • Reference types: Complex data types that hold a memory address that points to data stored elsewhere in memory. Examples of reference types include classes, interfaces, strings, arrays, and delegates.

The easiest way to know the difference between value types and reference types is to look at how you declare and initialize variables of either type. Reference types use the new operator, whereas value types don't.

For example, the following code declares a variable of the value type integer by using the C# keyword int:

int i = 1;

Note

When you create a variable with a reference data type, you use the new operator. The code that follows creates a new variable o of the data type System.Object. Recall that System.Object is the root data type in the CTS:

System.Object o = new System.Object();

Throwing it on the stack or the heap

Value types and reference types are stored differently in memory, which contributes to why they're used differently. Value types are stored on the stack, and reference types are stored on the managed heap. The stack — more specifically, the call stack — is an area of memory set aside for managing the execution of your program. You can visualize the stack as one memory address stacked on top of another. The heap, however, is a large pool of memory where objects that require longer life spans can live. (Longer life span means that the object needs to live beyond the execution of a single function on the call stack.)

The stack is more efficient to access than the heap because the stack discards a variable stored in memory as soon as the variable goes out of scope. Variables stored in the heap, however, are managed by the .NET garbage collector. The new operator requests storage to be allocated on the heap for the variable. The garbage collector clears the variable out of memory when it determines that the variable is no longer being used by your program, which might not correspond to the point in time when your program stops using the variable.

By being stored on the stack, value types are directly accessible. Reference type variables, however, return a reference to an address on the heap where the variable's value is actually stored. Figure 2-1 shows an example of value types and reference types in memory. The reference to a reference type is stored on the stack, which is how your program knows how to access the variable.

Value types are stored on the stack, and reference types are stored on the heap.

Figure 2-1. Value types are stored on the stack, and reference types are stored on the heap.

Completing your assignments

Another key difference between value types and reference types is how they're handled by assignments. When you assign a variable of a value type, the contents of the value type are copied. When you assign a reference type, only the reference is passed along.

The difference in how value types and reference types are assigned is attributable to how they're stored in memory. A reference type stores only a memory address that points to the actual value. When you copy a reference type, you copy the memory address stored on the stack.

The following code sample uses value types, and the value for variable i is copied to the variable j:

private void TestValueTypes()
{
  int i;
  int j;
  i = 8;
  j = i;
  i = 5;
}

When this code executes, j has the value 8, and i has the value 5. j and i are independent variables. The value from i is copied to j.

Now, look at how code using reference types does this. Note that the variables start out pointing to two separate values but wind up pointing to the same value:

private void TestReferenceTypes()
{
  System.Data.DataSet ds1 = new DataSet("DataSet 1");
  System.Data.DataSet ds2 = new DataSet("DataSet 2");
  ds2 = ds1;
  ds1.DataSetName = "My DataSet";
}

The statement ds2 = ds1; assigns the value referenced in the variable ds1 to the variable ds2. What happens to the value originally referenced by ds2? It's still there, but it can no longer be accessed because ds2 let go of it, as you can see in Figure 2-2. Eventually, the garbage collector recognizes that the object is no longer in use and destroys it.

Multiple variables can reference the same value.

Figure 2-2. Multiple variables can reference the same value.

You may think it's silly that someone would create two variables and then assign one to the other. After you start creating an application, you'd be surprised how easy it is to pass a variable by reference and not understand why your data isn't what you expected.

You need to understand the differences between value types and reference types. Table 2-1 compares value types and reference types.

Table 2-1. Comparison of Value Types and Reference Types

Category

Value Types

Reference Types

Data accessibility

Directly accessible

Accessed through a reference to the data

Memory allocation

Stack

Managed heap

When is memory freed?

When variable is destroyed

When garbage collection determines no longer in use

Garbage collection

Doesn't use

Uses garbage collection

Initialization

No special initialization required

Must use new operator

Default value

Initialized as zero

Initialized as null

Null values

Can never be null

Throws an exception when you try to use a null reference

Assignment

Assignment copies the value

Assignment passes a reference to the value

Base class

Derives from System.ValueType

Derives from System.Object

Conversion

Can be converted to reference type

Can be converted to value type in some cases

Popular value types

If you've done any programming at all, you're probably familiar with data types, such as char and integer. These are value types in the .NET Framework. The CTS provides a set of built-in value types.

Your programming language provides a set of primitive data types that map to the built-in value type in the CTS. Table 2-2 lists the value types in .NET and their respective keywords in Visual Basic and C#. When you use one of these keywords as a data type in your program, it's compiled as the underlying .NET data type listed.

Note

A primitive data type is a type that the programming language's compiler natively understands.

Table 2-2. Built-In Value Types and Their Language Keywords

.NET Data Type

Visual Basic Keyword

C# Keyword

Description

System.Boolean

Boolean

bool

True or false

System.Byte

Byte

byte

Unsigned integer with values ranging from 0 to 255

System.Char

Char

char

Represents characters in the Unicode Standard, such as the letter a

System.Decimal

Decimal

decimal

Decimal numbers ranging from −79,228,162,514,264,337,593,543,950,335 to +79,228,162,514,264,337,593,543,950,335

System.Double

Double

double

Fifteen decimal points of precision for binary floating-point arithmetic

System.Int16

Short

short

Signed integer with values ranging from −32,768 to +32,767

System.Int32

Integer

int

Signed integer with values ranging from −2,147,483,648 through +2,147,483,647

System.Int64

Long

long

Signed integer with values ranging from −9,223,372,036,854,775,808 through +9,223,372,036,854,775,807

System.Sbyte

Sbyte

sbyte

Signed integer with values ranging from −127 to +127

System.Single

Single

float

Seven decimal points of precision for binary floating-point arithmetic

System.UInt16

Ushort

ushort

Unsigned integer values ranging from 0 to 65,535

System.UInt32

UInteger

uint

Unsigned integer values ranging from 0 to 4,294,967,295

System.UInt64

Ulong

ulong

Unsigned integer values ranging from 0 to 18,446,744,073,709,551,615

You might notice that many data types represent numbers. The amount of memory used by each of these data types corresponds to the range of values that the data type can store.

Tip

Use the System.Decimal data type for financial calculations where rounding errors can't be tolerated.

Most of the .NET programming languages have a keyword for a data type that represents strings. Strings aren't value types: They're reference types. See the next section, "Popular reference types," to read more about strings.

In addition to the value types listed previously, the .NET Framework includes two other programming elements that define value types:

  • Structures: A structure is a data type comprising other data types. You use a structure to consolidate other data types into a single, named data type. Examples of two commonly used structures provided by the .NET Framework are System.DateTime and System.Guid.

    Tip

    The Visual Basic keyword that corresponds to System.DateTime is Date. C# doesn't have a keyword. guid (rhymes with squid) is short for globally unique identifier. You use a guid any time you need a unique identifier.

    You use structures to create your own data types. See the section "Creating Your Own Types" for examples of using structures.

  • Enumerations: An enumeration defines a set of constants that can be accessed by the name applied. System.DayOfWeek is an example of an enumeration that enumerates the days of the week, as shown in Figure 2-3. Enumerations derive from System.Enum.

Enumerations define a set of constants.

Figure 2-3. Enumerations define a set of constants.

Popular reference types

All the classes in the .NET Framework are reference types, so-called because the variable holds a reference to the value — not the actual value itself.

All classes in the CTS derive from System.Object. You could say that makes System.Object the most popular reference type. Hopefully, however, you aren't actually declaring your variables by using the System.Object type. Although doing so isn't incorrect, try to use the most specific data type possible.

C# provides the keyword object, and Visual Basic uses Object to map to the System.Object type in CTS.

One of the most popular reference types is System.String. Many people think that System.String is a value type, but it's actually an array of characters. That's probably because you don't have to use the new operator when you create a new string. You declare and use a string variable similarly to how you use value types, such as integer and Boolean.

Visual Basic and C# both provide a keyword to represent System.String. The following code declares and initializes a string variable:

VB:

Dim s As String = "Hello"

C#:

string s = "Hello";

Another popular reference type is System.Exception. Any time your program throws an exception, the exception you see is a reference type derived from System.Exception.

See the section "Browsing Types," later in this chapter, to see how you can find more reference types in the .NET Framework.

Creating Your Own Types

Developers long ago figured out that describing a business domain strictly in terms of ints and chars isn't easy. You can't bend the business to fit the limitations of a programming language. No, instead, you must use a programming language that allows you to model whatever real-world problem you're trying to solve.

The .NET Framework allows you to create your own data types. You can use your own data types to

  • Model your business in your software.

  • Provide utility features.

  • Customize existing types that don't quite fit your needs.

You can use the following kinds of data types to create your own data types:

  • Classes: Classes are reference types that derive from System.Object. Class types define the data and behavior of a variable. In other words, classes define the data that a variable can store and provide procedures that act on that data. For example, a Customer class may store a customer's name and unique identifier. It may include an AccountBalance() procedure that returns the customer's current balance.

    Tip

    Visual Basic and C# provide the Class statement and class keyword, respectively, for creating your own class data types. See the next chapter for more information on creating classes.

  • Structures: Structures are value types that derive from System.ValueType. Structures can store virtually all the same data and behaviors as a class.

    Tip

    You use the Structure statement in Visual Basic to create a structure. C# provides the struct keyword:

    • To create a Customer structure in Visual Basic, type the following code:

      Structure Customer
        Dim m_firstName As String
        Dim m_lastName As String
        ReadOnly Property Name() As String
          Get
           Return m_firstName + " " + m_lastName
          End Get
        End Property
        WriteOnly Property FirstName()
          Set(ByVal value)
            m_firstName = value
          End Set
        End Property
        WriteOnly Property LastName()
          Set(ByVal value)
            m_lastName = value
          End Set
        End Property
      End Structure
    • To create a variable by using the Customer data type, type the following code:

      Dim cust As New Customer
    • To assign values to a variable, type the following code:

      cust.FirstName = "John"
      cust.LastName = "Smith"

    The following line of code assigns the variable's Name property to a text box:

    txtCustomerName.Text = cust.Name

    Notice that the cust variable accesses only the structure's properties, and not the variables declared at the beginning of the structure. The structure's properties, in turn, access the variable declared at the top of the structure. The variables and properties of the structure are the members of the structure. See the next chapter to read more about a data type's members.

  • Enumerations: Enumerations are value types that derive from System.ValueType. You define a set of constants, such as the days of the week, in an enumeration. You can use the System.Enum data type to access additional features of enumerations.

    Visual Basic provides the Enum statement, and C# uses the enum keyword for declaring enumerations. The following code shows an enumeration in C#:

    enum Fiber
    {
      Angora,
      Mohair,
      Wool
    }

With object-oriented programming (OOP) techniques, you can extend virtually all the types provided in the .NET Framework to meet your specific needs. See Chapter 3 in Book III for more information about OOP.

Tip

You often create user-defined types in a class library project. You can reference the class library in a Windows project or Web application when you need to use your user-defined types.

You can use your class and structure data types just like you do any other data type. You can declare them as variables, pass them as parameters, and return them from procedures. Use enumerations any time you need to reuse a set of values throughout your application.

At first blush, you may think there isn't much difference between structures and classes. Recall, however, that structures are value types and classes are reference types. As a result, structures often use less memory than classes. Each time a value type like a structure is passed around in a program, a copy of the structure is made. So, what starts out using less memory could end up consuming quite a bit. For this reason, you'll often find that classes are used more than structures. Even though classes are initially more expensive to create because there is both memory allocation and startup code to execute, their memory usage is often more economical throughout their lifetime.

In general, you should create structures when the data type you're creating is small in size, like an integer, and you expect it to be short-lived.

When There's More than One

Quite often, you need to handle more than just one of something. Rarely does your business have just one customer or one product. You're usually dealing with sets of things. The .NET Framework provides many data types for dealing with situations when you have more than one item.

Tip

Data types that can handle sets of data are often referred to as data structures or collections.

The collection-related data types provided by the .NET Framework often allow you to

  • Add, remove, and modify individual elements.

  • Copy elements to another collection.

  • Sort and index elements.

  • Iterate through a set of elements.

The .NET Framework provides several data types you can use to manage collections. The two biggies are

  • Array class: An array is a set of data of all the same data type. You set the size of the array when you declare it. Arrays have been the staple data structure for a long time.

    Tip

    Picture an array as an Excel spreadsheet. A one-dimensional array is like a single row in the spreadsheet. A multidimensional array has more than one row.

  • System.Collections namespace: Other kinds of collections, such as lists and hashtables, are found in the System.Collections namespace.

Note

A namespace references a set of data types. A namespace isn't a data type itself. No System.Collections data type exists in the .NET Framework. Rather, you use the System.Collections namespace to access data types used to manage collections.

Another kind of data structure provided by the .NET Framework is the ADO.NET, which is an in-memory representation of a database with tables, columns, and rows. For more on DataSets, turn to Book V, Chapter 3.

Using arrays

The .NET Framework provides the System.Array class for creating arrays. An array defines a set of data that all have the same data type. You can define an array to use any kind of data type, such as a set of integers or a set of strings. You can even define an array by using your own custom data types.

Note

All items in an array must be of the same data type.

An array has the following properties:

  • Elements: Each item that you add to an array is an element of the array. The data type of the element is the element type.

  • Index: The index is the position of each element in the array. Arrays use zero-based indexes, so the first value in an array has an index of zero.

  • Length: This is the total number of elements in the array.

  • Rank: This is the number of dimensions in the array. A one-dimensional array has one row of data. A two-dimensional array has multiple rows.

  • Bounds: These are the lower and upper bounds of an array that define the starting and ending index for an array's elements. For example, an array with four elements has a lower bound of zero and an upper bound of three.

Declaring arrays is similar to declaring other types of data. In Visual Basic, you append parentheses to the variable's identifier:

Dim dailyWeights() As Integer

To declare a multidimensional array in Visual Basic, you place a comma inside the parentheses for each additional dimension, such as

Dim dailyWeights(,) As Integer

In C#, you append brackets to the element's data type when you declare an array:

int[] dailyWeights;

Similar to Visual Basic, you use commas to create multidimensional arrays:

int[,] dailyWeights;

Note

You can also create arrays of arrays, which are called jagged arrays. You add extra sets of parentheses or brackets for each nested array. A declaration for a jagged array in Visual Basic looks like this:

Dim dailyWeights()() As Integer.

Declaring an array doesn't actually create the array. Because arrays are reference types, you use the new operator to create the array and assign it to the variable you declare. For example, to create an array with five elements and assign it to the one-dimensional dailyWeights array using Visual Basic, you'd type

dailyWeights = New Integer(4) {}

Recall that arrays have a zero-based index. Inside the parentheses, you place the array's upper bound, which is 4 in this example. The array's length is 5 because you start counting at zero.

Use the curly braces to place values into the array, as the following Visual Basic code shows:

dailyWeights = New Integer(4) { 155, 153, 154, 152, 150 }

Here's the equivalent statement in C#:

dailyWeights = new int[5] { 155, 153, 154, 152, 150 };

You may have noticed some subtle differences in syntax between Visual Basic and C#. Most notably, in the Visual Basic statement, you use the upper bound; in C#, you use the array's length. If you switch back and forth a lot between the two languages, maybe you can get a tattoo so that you can keep it straight.

Tip

You don't need to size an array when you're initializing the array in the same statement. Supplying five values automatically creates an array of length = 5. For example, the following statements are equivalent:

dailyWeights = new int[5] { 155, 153, 154, 152, 150 };
dailyWeights = new int[] { 155, 153, 154, 152, 150 };

Here are three steps to using arrays:

  1. Declare the array variable.

  2. Create the array.

  3. Initialize the array with values.

You can perform each step discretely or combine all three steps into one statement, as shown in the following C# statement:

int[] dailyWeights = new int[5] { 155, 153, 154, 152, 150 };

The equivalent statement in Visual Basic is

Dim dailyWeights() As Integer = New Integer(4) {155, 153, 154, 152, 150}

You supply three pieces of information to declare and create an array:

  • The element's data type

  • Its rank

  • The upper bound or the size of the array

To access the elements in an array, use an indexer to specify the position of the element you wish to access. For example, the following C# code accesses the third element in an array of integers:

dailyWeights[2] = 175;

Note

Arrays use a zero-based index, which means that you start counting from zero.

Using System.Collections

The .NET Framework provides many kinds of collections that you can use when you need to handle more than one of something at a time. Table 2-3 lists the specialized collection types you can find in the System.Collections namespace.

You can group collection data types in the System.Collections namespace based on the mechanism used to access elements in the collection:

  • Indexed: These access elements by using their position in the list of elements.

  • Keyed: These access elements by using the key in a key/value pair.

  • Neither indexed nor keyed: Data types provide access methods other than an indexer or a key.

Note

Collections that use indexes are lists. Keyed collections are dictionaries.

Table 2-3. Data Types in the System.Collections Namespace

Accessor

Collection Type

Data Type

Description

Example

Both

Dictionary

SortedList

A set of key/value pairs sorted by the key

A glossary of terms

Indexed

Collection

BitArray

An array of Boolean values

Whether your dog responds to the Come command given successively at the bark park

Indexed

List

ArrayList

An array of variable size

The number of debits to your bank account in the next 30 days

Keyed

Dictionary

Hashtable

A set of key/value pairs sorted by the key's hash number

The movies being shown at a theater

Neither

Collection

Stack

Last-in, first-out (LIFO) list

A pile of football players on a quarterback

Neither

Collection

Queue

First-in, first-out (FIFO) list

Line at the grocery store

Tip

The System.Collections namespace defines the DictionaryEntry structure, which represents a key/value pair in a dictionary collection type. See the upcoming section "Iterating through arrays and collections" to see a code sample that uses the DictionaryEntry structure.

The collection data types found in the System.Collection namespace are all classes. You use the new operator to create a new instance of a collection. The following code shows a Hashtable:

Dim ht As New Hashtable

Important actions you take on collections, such as a Hashtable, include adding and removing elements. A Hashtable is a key-based collection, so you add elements by using a key/value pair. The following code sample shows how to add elements to a Hashtable:

ht.Add("Screen 1", "Shane")
ht.Add("Screen 2", "My Friend Flicka")

Use the Remove method to remove an element from the hashtable:

ht.Remove("Screen 1")

You supply the key when you want to remove the element.

Of course, you aren't limited to using just primitive types and strings in your collections. You can use any data type, including your user-defined data types. For example, instead of placing a movie title in the hashtable, you could place a value created from a Movie class. The Movie class might store a movie's title, actors, release date, and show times. See the earlier section on creating your own types for more information.

The .NET Framework provides two other namespaces for using collection data types:

  • System.Collections.Generic: Provides data types that allow you to create type-specific collections. In a type-specific or strongly typed collection, you specify in advance the type of data that can be placed into the collection.

    Type-specific collections are generic collections. All the collections listed in Table 2-3 have generic counterparts.

    For example, the Dictionary data type is the generic version of the Hashtable data type. To create a keyed collection that accepts only the Customer data type, you use the Dictionary data type, as shown here:

    Dim invoiceCustomers As New Dictionary(Of String, Customer)

    You add objects of the type Customer with a string key that represents their customer ID by using the Add method, as shown in the following code:

    invoiceCustomers.Add("1234", cust)
  • System.Collections.Specialized: Contains a set of strongly typed or specialized collections. For example, StringDictionary is a generic hashtable that works only with the data type string. If you try to put some other data type (such as an integer) into a StringDictionary, you receive an exception.

Tip

Collections are used extensively throughout the .NET Framework. For example, Windows Forms and Web Forms have a set of control collections. You can iterate through a form's collection of controls to add controls or find a specific control you wish to use.

Iterating through arrays and collections

An important task when working with collections is to be able to step through the elements within the collection. C# and Visual Basic provide the foreach and For Each statements, respectively, for iterating through a collection.

For example, the following code shows iterating through an ArrayList using For Each in Visual Basic:

Dim list As New ArrayList
list.Add(1)
list.Add(2)
list.Add(3)
For Each i as Integer In list
  Dim j As Integer
  j = j + i
Next

The For Each statement in the preceding sample executes once for each element in the ArrayList for a total of three times. On third execution, it's j = 6 because 1 + 2 + 3 = 6.

Note

The variable j is in scope only while execution is inside the For Each statement. After execution moves off the Next statement the third time, you can no longer access j. If you want to use the variable j outside the For Each statement, you need to declare it outside the For Each statement.

Keyed collections use the DictionaryEntry structure for iterating through values, as the following C# code sample shows:

Hashtable ht = new Hashtable();
ht.Add("Screen 1", "Shane");
ht.Add("Screen 2", "My Friend Flicka");
foreach (DictionaryEntry de in ht)
{
   lstMovies.Items.Add(de.Key + ":" + de.Value);
}

Figure 2-4 shows an example of the key/value pair from the hashtable in a list box.

Iterate through the hashtable to display key/value pairs in a list box.

Figure 2-4. Iterate through the hashtable to display key/value pairs in a list box.

Collections in the real world

You have many choices for working with collections. Here are some guidelines to help you decide:

  • Choose arrays over collections when the number of elements is known and not expected to grow.

  • Choose collections over arrays any time you find yourself looking for methods, such as Add, Remove, Item, or Count.

  • Choose generic collections when you know in advance the data type you want to store in the collection. Generic collections perform better than nongeneric collections because their code doesn't have special cases to consider.

Besides selecting the best type of collection for the job, you must also consider how you'll actually use the collection in your code. If you're creating your own data types, you need to consider how to manage multiples of your data types. You have a few options:

  • Extend the collections classes provided by .NET.

  • Wrap an existing collection by creating a class to manage it.

In most cases, you'll probably wrap an existing collection when you want to provide your own custom collections. You have two approaches to wrapping an existing collection:

  • Create a new data type that wraps the collection.

    For example, you can create a data type called MovieCollection that allows you to handle a collection of movies without thinking about the underlying collection actually used.

  • Use a collection in an existing data type. An alternative approach to creating a separate wrapper collection is to include the collection in your data type. For example, you could create a generic collection of Movie types that you access from within your Movie type.

Converting Types

An important task when working with variables is converting a variable of one data type to another data type. For example, a variable with the Boolean value True isn't the same as a variable with the string value true. They may look the same, but the language sees the Boolean variable as either on or off, and the string variable as an array of characters.

Note

A variable's data type determines how much memory is allocated for the variable. When you convert from one data type to another, you're essentially asking the computer to give you more or less memory. Here are the two kinds of conversions that you can perform:

  • Widening: Going from a smaller data type to a larger data type

  • Narrowing: Going from a larger data type to a smaller data type

Warning

You risk data loss with narrowing conversions.

The syntax that you use to widen or narrow depends on whether the conversion is

  • Implicit: Implicit conversions don't require any special syntax in order for the conversion to occur. For example, the following code implicitly converts a Boolean value to a string value:

    Dim b As Boolean = True
    Dim s As String = b
  • Explicit: Any time you have to use special syntax to convert a variable, you make an explicit conversion. Explicit conversions are often necessary when you perform a narrowing conversion.

The .NET Framework provides the System.Convert class, which you can use to explicitly convert from one type to another. For example, the following code converts a string to an integer in C#:

string s = "1000";
int i = System.Convert.ToInt32(s);

C# provides the cast operator for performing explicit conversions. The following code converts from an integer value to a byte, which is a narrowing conversion:

int i = 255;
byte b = (byte)i;

Note

Narrowing conversions can cause loss of data. For example, take a look at the following code sample:

int i = int.MaxValue;
byte b = (byte)i;

The maximum value of an integer data type is north of two million. So what's the value in the variable b after the conversion of integer i? It's 255. A byte holds values from only 0 to 255.

Visual Basic provides the CType function, which you can use for explicit conversions. You pass an expression to convert and the data type to convert to the CType function, as shown here:

Dim s As String = "255"
Dim b As Byte = CType(s, Byte)

Visual Basic has type conversion functions for each primitive data type and a function each for converting reference types and strings. For example, the following code is equivalent to using CType in the preceding sample:

Dim s As String = "255"
Dim b As Byte = CByte(s)

Any implicit conversion can be explicitly stated. The following code explicitly converts a Boolean value to a string value:

Dim b As Boolean = True
Dim s As String = Convert.ToString(b)

Tip

There's no harm in using an explicit conversion in place of an implicit conversion. You should use explicit conversion any time you want to make it clear to readers of your code that a conversion is occurring.

You aren't restricted to converting between primitive types and strings. You can convert any data type in the .NET Framework, your programming language, or your user-defined data types. System.Convert, CType, and the cast operator all allow any kind of data type for making conversions. The catch, of course, is that the data types you're converting must be compatible.

In order for a conversion to be successful, a conversion operator must be defined for the type you want to go from to. See the Visual Studio documentation for a list of available conversions for your language.

Note

Converting value types (such as integers and Booleans) to reference types (such as strings and objects) is boxing. Boxing and its converse — unboxing — occur any time you use a value type when a reference type is expected. Passing value types as parameters when a reference type is expected is one example of when a value type is boxed.

Note

All data types derive from System.Object; therefore, you can convert a variable of any data type to System.Object.

You can use the System.Type class to find out more information about a data type, such as the data type from which the data type is derived, as well as whether the data type is a value type or a reference type.

Meet the Nullable Types

Often, when working with values from databases, you encounter null values, which are undefined values. A null value can make your program blow up when the program is expecting to see an integer or a Boolean or a string. To help you process and anticipate null values, the .NET Framework includes a data type called System.Nullable.

You use System.Nullable to tell your program to accept a null value in your variable. System.Nullable provides the following properties:

  • HasValue: Returns a true or false value indicating whether the variable has a value or is null.

  • Value: Retrieves the variable's value. You use the HasValue property to test that the variable contains a value before using the Value property.

System.Nullable works with value types. Values types are primitive data types, such as integer and char. By definition, value types can't store null values. Reference types, such as strings, can store null values. As a result, it's not necessary for System.Nullable to work with reference types.

Warning

That's not to say that null reference types can't wreak the same kind of havoc in your program as trying to assign a null value to a value type. You should test your reference types for null values before you try to access the value.

When you declare a nullable value type, you tell System.Nullable which value type you wish to use. The following Visual Basic code sample creates a nullable integer:

Dim i As System.Nullable(Of Integer)

The equivalent declaration in C# is

System.Nullable<int> i;

C# provides the question mark (?) shortcut operator you can use when declaring nullables. The following statement is equivalent to the preceding statement:

int? i;

By declaring a variable as nullable, you can use the HasValue property to test for a null value. In the following code sample, if a nullable integer i has a value, the value is returned. Otherwise, the procedure returns 0 (zero).

int checkValue(int? i)
{
  if (i.HasValue == true)
     return i.Value;
  else
     return 0;
}

Browsing Types

The .NET Framework has hundreds of data types. Your own code base may have dozens — possibly even hundreds — of data types. Visual Studio provides Object Browser for perusing the vast libraries of data types available to you.

You use Object Browser any time you need to

  • Find a data type.

  • View the members of a data type, such as properties and methods.

  • View a description and get help for a data type.

You open Object Browser using the View menu or the key combination Ctrl+Alt+J. You don't need to have a project open to use Object Browser. Your open projects appear in Object Browser.

Setting the scope

You'd be quickly overwhelmed if you had to look at all the data types in Object Browser at once. Instead, Object Browser allows you to limit the scope of the types you view at any one time to the following:

  • .NET Framework

  • Third-party components

  • Your own projects and components

To view only the components in the .NET Framework, follow these steps:

  1. Press Ctrl+Alt+J to open Object Browser.

  2. Click the Browse drop-down list on the Object Browser toolbar.

    A list of browsing scopes appears.

    Object Browser displays data types from these browsing scopes:

    • All Components: Displays the data types from the other options

    • .NET Framework: Displays data types found in the .NET Framework

    • My Solution: Displays data types created and referenced in the open solution

    • Custom Component Set: Displays data types from a third-party component.

  3. Select .NET Framework 4 from the drop-down list.

    The assemblies in the .NET Framework appear in the Objects pane on the left, as shown in Figure 2-5.

Tip

Use the My Solution browsing scope to view the assemblies referenced by your project.

Alternatively, you can use the Object Browser Search drop-down list to search for a word. Search is limited to the browsing scope selected in the Browse drop-down list. See Chapter 7 of Book III for an example of using Search in Object Browser.

The assemblies from the selected browsing scope appear in the Objects pane.

Figure 2-5. The assemblies from the selected browsing scope appear in the Objects pane.

Setting the view

Object Browser displays data types of all kinds, including classes, enumerations, and interfaces. By default, data types appear in assembly containers; however, there are many different views, such as

  • Assemblies: The physical files in which the data type is defined. Assemblies are the DLL files that are output when your source code is built.

  • Namespaces: The logical namespace in which the data type is defined

  • Object types: The kind of data type, such as class, enumerator, or structure

To view the data types by namespaces

  1. Right-click inside the Objects pane.

    A shortcut menu appears.

  2. Choose View Namespaces.

    The data types are grouped by their namespaces.

    To group the data types by assembly, repeat Step 1 and choose View Containers from the shortcut menu.

    Choose Group by Object Type from the shortcut menu to group the data types by the type of data type.

To view the physical assembly file where data is defined

  1. Group the data types by assembly, as described in the preceding set of steps.

  2. Click the assembly you wish to view.

    The assembly's name, path, and attributes appear in the Description pane. Figure 2-6 shows the assembly information for the System assembly.

Click an assembly to view information about the assembly.

Figure 2-6. Click an assembly to view information about the assembly.

Viewing data types

You can use Object Browser to view all kinds of information about data types, including the following data types:

  • Assembly and namespace

  • Members, such as properties and methods

  • Base data type and derived data types

Here's how to view a data type in Object Browser:

  1. Set your browsing scope and view.

  2. Click the plus sign (+) next to the container of data types.

    Depending on how you set up your view, you might also have to expand the Namespaces and Object Type folders to access the actual data types.

  3. Click the data type to view its members and description.

For example, to access the System.Enum data type with the data types grouped by assemblies and object types, follow these steps:

  1. Click the plus sign (+) next to the mscorlib assembly.

    The Namespaces folder appears.

    The mscorlib assembly contains the core namespaces of the .NET Framework.

  2. Click the plus sign (+) next to the Namespaces folder.

    A list of namespaces found in the mscorlib assembly appears.

  3. Click the plus sign (+) next to the System Namespace folder.

    A list of Object Type folders appears.

    Note that the .NET Framework has a System assembly and a System namespace. The System namespace spans across both the mscorlib and System assemblies.

  4. Click the Structures folder.

    A list of structure data types appears.

  5. Click the Enum data type.

    The members and description appear in the browser.

    To view a member's description, click the member, as shown in Figure 2-7.

Click a data type's member to view its description.

Figure 2-7. Click a data type's member to view its description.

A data type's base data type can give you clues about whether the type is a value type or reference type. Viewing the type's derived data types shows you more specific implementations of the data type that might be more appropriate for you to use.

You can view a data type's base type and any derived types by expanding the data type. In the case of the System.Enum data type, its base type is System.ValueType. Many types are derived from System.Enum, as shown in Figure 2-8.

Expand a data type to view its base types and derived types.

Figure 2-8. Expand a data type to view its base types and derived types.

You may have noticed that the data types, members, and other items in Object Browser have icons next to them. A different icon is used to represent each kind of data type, such as classes or structures. Search for the topic "Class View and Object Browser Icons" in the Visual Studio Help documentation for a summary of the icons used.

Viewing source code

Visual Studio provides several tools for browsing and navigating source code, including

  • Class View: Use Class View to display a hierarchical view of the solution you're developing.

  • Code Definition: This displays a read-only view of the source code for the selected object.

You use the Code Definition window in conjunction with the Class View or Object Browser to view an object's source code. When viewing objects from outside your project, the Code Definition window displays only the source code's type and method declarations and comments. You can't actually view the source code that implements the object.

Tip

The Code Definition window doesn't work in Visual Basic.

To use the Code Definition window with Class View, follow these steps:

  1. Open a C# project in Visual Studio.

    If you don't have an existing C# project, you can open one of the C# Starter Kits, such as the Movie Collection Starter Kit, from the New Project window.

  2. Press Ctrl+Shift+C to open Class View.

    The project appears in the Class View window.

  3. Choose View

    Viewing source code

    The Code Definition window appears.

  4. Expand the classes in Class View and click one of the objects.

    The object's methods appear in the bottom pane of Class View.

    Class View and Object Browser use a number of icons to represent different kinds of objects. For example, the open and closing curly braces { } represent a namespace. See the topic "Class View and Object Browser Icons" in the Visual Studio documentation for a complete list of the icons used.

  5. Click one of the public methods.

    The method's source code appears in the Code Definition window, as shown in Figure 2-9.

Source code appears in the Code Definition window.

Figure 2-9. Source code appears in the Code Definition window.

The Code Definition window is a read-only view of the source code. To open the source code file in the Code Editor, right-click the item in the Class View window and choose Go to Definition from the contextual menu.

To view the object in Object Browser, right-click the object in the Class View window and choose Browse Definition from the contextual menu.

Accessing Types in Your Source Code

Data types are logically organized into namespaces but physically organized into assemblies. In order to consume types in your source code, you need to know how to access the physical and logical paths to data types.

  • References provide access to the physical assembly files where the types can be found.

  • Namespaces provide access to the logical path of the type within the referenced assembly.

You access the physical file where types are stored by adding a reference to the type. Visual Studio provides the ability to add new references and manage existing references in your project.

When you create a new project, Visual Studio automatically adds a number of references to common, physical DLL files you may need to use in your project. You might remove any reference that you aren't using.

After you add a reference to the physical assembly where the type resides, you must also supply the logical namespace to access the type and its members. You can type the namespace in source code. If you have properly referenced the assembly, IntelliSense pops up to help you find the namespaces you need.

You can also include namespace directives — at the top of your source code — to provide a shortcut to namespaces you wish to use in your source code. The namespace directive in C# is using, and it's Include in Visual Basic. These directives allow you to access types within a namespace without fully qualifying the namespace every time.

For example, say you want to create a new DataSet in your source code. The DataSet type is found in the System.Data namespace. Using the fully qualified namespace looks like this in C#:

System.Data.DataSet ds = new System.Data.DataSet();

However, if you add the following using directive at the top of your source code file

using System.Data;

you can create a new DataSet:

DataSet ds = new DataSet();

Using namespace directives often saves space in your code and makes your code easier to read.

Another approach is to create an alias for the namespace so that it quickly identifies that the type is being referenced from another namespace. To create an alias in C#, type the following:

using data = System.Data;

Now, you can qualify the type by using the alias, as shown in the following code:

data.DataSet ds = new data.DataSet();

The same code in Visual Basic appears as

Imports data = System.Data
Dim ds as new data.DataSet
..................Content has been hidden....................

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