Chapter 3. Types, Storage, and Variables

A C# Program Is a Set of Type Declarations

If you were to broadly characterize the source code of C and C++ programs, you might say that a C program is a set of functions and data types, and that a C++ program is a set of functions and classes. A C# program, however, is a set of type declarations.

  • The source code of a C# program or DLL is a set of one or more type declarations.

  • For an executable, one of the types declared must be a class that includes a method called Main.

  • A namespace is a way of grouping a related set of type declarations and giving the group a name. Since your program is a related set of type declarations, you will generally declare your program inside a namespace you create.

For example, the following code shows a program that consists of three type declarations. The three types are declared inside a new namespace called MyProgram.

namespace MyProgram                         // Create a new namespace.
   {

      DeclarationOfTypeA                       // Declare a type.


      DeclarationOfTypeB                       // Declare a type.


      class C                                  // Declare a type.
      {
         static void Main()
         {
            ...
         }
      }
   }

Namespaces will be covered in more detail in Chapter 10.

A Type Is a Template

Since a C# program is just a set of type declarations, learning C# consists of learning how to create and use types. So the first thing you need to do is to look at what a type is.

You can start by thinking of a type as a template for creating a data structure. It is not the data structure itself, but it specifies the characteristics of objects constructed from the template.

A type is defined by the following elements:

  • A name

  • A data structure to contain its data members

  • Behaviors and constraints

For example, Figure 3-1 illustrates the components of two types: short and int.

A type is a template.

Figure 3-1. A type is a template.

Instantiating a Type

Creating an actual object from the type's template is called instantiating the type.

  • The object created by instantiating a type is called either an object of the type or an instance of the type. The terms are interchangeable.

  • Every data item in a C# program is an instance of some type—either a type provided by the language, provided by the BCL or another library, or defined by the programmer.

Figure 3-2 illustrates the instantiation of objects of two predefined types.

Instantiating a type creates an instance.

Figure 3-2. Instantiating a type creates an instance.

Data Members and Function Members

Some types, such as short, int, and long, are called simple types, and can only store a single data item.

Other types can store multiple data items. An array, for example, is a type that can store multiple items of the same type. The individual items are called elements, and are referenced by a number, called an index. I will describe arrays in detail in Chapter 14.

Types of Members

Other types, however, can contain data items of many different types. The individual elements in these typesare called members, and, unlike arrays, in which each member is referred to by a number, these members have distinct names.

There are two types of members: data members and function members.

  • Data members store data that 0_marker-1183485 relevant to the object of the class or to the class itself.

  • Function members execute code. Function members define how the type can act.

For example, Figure 3-3 shows some of the data members and function members of type XYZ. It contains two data members and two function members.

Types specify data members and function members.

Figure 3-3. Types specify data members and function members.

Predefined Types

C# provides 15 predefined types, which are shown in Figure 3-4 and listed in Tables 3-1 and 3-2. They include 13 simple types and 2 non-simple types.

The names of all the predefined types consist of all lowercase characters. The predefined simple types include the following:

  • Eleven numeric types, including

    • Various lengths of signed and unsigned integer types.

    • Floating point types--float and double.

    • A high-precision decimal type called decimal. Unlike float and double, type decimal can represent decimal fractional numbers exactly. It is often used for monetary calculations.

  • A Unicode character type, called char.

  • A Boolean type, called bool. Type bool represents Boolean values and must be one of two values—either true or false.

Note

Unlike C and C++, numeric values do not have a Boolean interpretation in C#.

The two non-simple types are the following:

  • Type string, which is an array of Unicode characters.

  • Type object, which is the type on which all other types are based.

The predefined types

Figure 3-4. The predefined types

More About the Predefined Types

All the predefined types are mapped directly to underlying .NET types. The C# type names are just aliases for the .NET types, so using the .NET names works fine syntactically, although this is discouraged. Within a C# program, you should use the C# names rather than the .NET names.

The predefined simple types represent a single item of data. They are listed in Table 3-1, along with the ranges of values they can represent and the underlying .NET types to which they map.

Table 3-1. The Predefined Simple Types

Name

Meaning

Range

.NET Framework Type

Default Value

sbyte

8-bit unsigned integer

−128–127

System.SByte

0

byte

8-bit unsigned integer

0–255

System.Byte

0

short

16-bit unsigned integer

−32,768–32,767

System.Int16

0

ushort

16-bit unsigned integer

0–65,535

System.UInt16

0

int

32-bit signed integer

−2,147,483,648–2,147,483,647

System.Int32

0

uint

32-bit unsigned integer

0–4,294,967,295

System.UInt32

0

long

64-bit signed integer

−9,223,372,036,854,775,808–9,223,372,036,854,775,807

System.Int64

0

ulong

64-bit unsigned integer

0–18,446,744,073,709,551,615

System.UInt64

0

float

Single-precision float

1.5×10−45–3.4×1038

System.Single

0.0f

double

Double-precision float

5×10−324–1.7×10308

System.Double

0.0d

bool

Boolean

true, false

System.Boolean

false

char

Unicode character

U+0000–U+ffff

System.Char

x0000

decimal

Decimal value with 28-significant-digit precision

±1.0×1028–±7.9×1028

System.Decimal

0m

The non-simple predefined types are somewhat more complex. Values of type string contain zero or more Unicode characters. The object type is the base class for all other types in the system, including the predefined, simple types. These are shown in Table 3-2.

Table 3-2. The Predefined Non-Simple Types

Name

Meaning

.NET Framework Type

object

The base class from which all other types are derived.

System.Object

string

A sequence of Unicode characters.

System.String

User-Defined Types

Besides the 15 predefined types provide by C#, you can also create your own user-defined types. There are six kinds of types you can create. They are the following:

  • class types

  • struct types

  • array types

  • enum types

  • delegate types

  • interface types

Types are created using a type declaration, which includes the following information:

  • The kind of type you are creating

  • The name of the new type

  • A declaration (name and specification) of each of the type's members—except for array and delegate types, which do not have named members

Once you have declared a type, you can create and use objects of the type just as if they were predefined types. Figure 3-5 summarizes the use of predefined and user-defined types. Using predefined types is a one-step process in which you simply instantiate the objects. Using user-defined types is a two-step process. You first declare the type and then instantiate objects of the type.

The predefined types require instantiation only. The user-defined types require two steps: declaration and instantiation.

Figure 3-5. The predefined types require instantiation only. The user-defined types require two steps: declaration and instantiation.

The Stack and the Heap

While a program is running, its data must be stored in memory. How much memory is required for an item, andwhere and how it is stored, depends on its type.

A running program uses two regions of memory to store data: the stack and the heap.

The Stack

The system takes care of all stack manipulation. You, as the programmer, don't need to do anything with it explicitly. But understanding its basic functions will give you a better understanding of what your program is doing when it is running, and allow you to better understand the C# documentation and literature.

The stack is an array of memory that acts as a last-in, first-out (LIFO) data structure. It stores several types of data:

  • The values of certain types of variables

  • The program's current execution environment

  • Parameters passed to methods

Facts About Stacks

The general characteristics of stacks are the following:

  • Data can only be added to and deleted from the top of the stack.

  • Placing a data item at the top of the stack is called pushing the item onto the stack.

  • Deleting an item from the top of the stack is called popping the item from the stack.

Figure 3-6 illustrates the functions and terminology of the stack.

Pushing and popping on the stack

Figure 3-6. Pushing and popping on the stack

The Heap

The heap is an area where chunks of memory can be allocated to store certain kinds of data. Unlike the stack, memory can be stored and removed from the heap in any order. Figure 3-7 shows a program that has stored four items in the heap.

The memory heap

Figure 3-7. The memory heap

Although your program can store items in the heap, it cannot explicitly delete them. Instead, the CLR's Garbage Collector (GC) automatically cleans up orphaned heap objects when it determines that your code will no longer access them. This frees you from what in other programming languages can be an error-prone task. Figure 3-8 illustrates the garbage collection process.

Automatic garbage collection in the heap

Figure 3-8. Automatic garbage collection in the heap

Value Types and Reference Types

The type of a data item defines how much memory is required to store it, the data members that comprise it, and the functions that it is able to execute. The type also determines where an object is stored in memory—the stack or the heap.

Types are divided into two categories: value types and reference types. Objects of these types are stored differently in memory.

  • Value types require only a single segment of memory—which stores the actual data.

  • Reference types require two segments of memory:

    • The first contains the actual data—and is always located in the heap.

    • The second is a reference that points to where in the heap the data is stored.

Data that is not a member of another type is stored as shown in Figure 3-9. For value types, data is stored on the stack. For reference types, the actual data is stored in the heap and the reference is stored on the stack.

Storing data that is not part of another type

Figure 3-9. Storing data that is not part of another type

Storing Members of a Reference Type Object

Figure 3-9 shows how data is stored when it is not a member of another type. When it is a member of another type, data might be stored a little differently.

  • The data portion of a reference type object is always stored in the heap, as shown in the figure.

  • A value type object, or the reference part of a reference type, can be stored in either the stack or the heap, depending on the circumstances.

Suppose, for example, that you have an instance of a reference type, called MyType, that has two members—a value type member and a reference type member. How is it stored? Is the value type member stored on the stack and the reference type split between the stack and the heap as shown in Figure 3-9? The answer is no.

Remember that for a reference type, the data of an instance is always stored in the heap. Since both members are part of the object's data, they are both stored in the heap, regardless of whether they are value or reference types. Figure 3-10 illustrates the case of type MyType.

  • Even though member A is a value type, it is part of the data of the instance of MyType, and is therefore stored with the object's data in the heap.

  • Member B is a reference type, and therefore its data portion will always be stored in the heap, as shown by the small box marked "Data." What's different is that its reference is also stored in the heap, inside the data portion of the enclosing MyType object.

Storage of data as part of a reference type

Figure 3-10. Storage of data as part of a reference type

Note

For any object of a reference type, all its data members are stored in the heap, regardless of whether they are of value type or reference type.

Categorizing the C# Types

Table 3-3 shows all the types available in C# and what kinds of types they are—value types or reference types. Each type will be covered later in the text.

Table 3-3. Value Types and Reference Types in C#

 

Value Types

 

Reference Types

Predefined Types

sbyte

byte

float

object

 

short

ushort

double

string

 

int

uint

char

 
 

long

ulong

decimal

 
 

bool

   

User-Defined Types

struct

  

class

 

enum

  

interface

    

delegate

    

array

Variables

A general-purpose programming language must allow a program to store and retrieve data.

  • A variable is a name that represents data stored in memory during program execution.

  • C# provides four categories of variables, each of which will be discussed in detail. These kinds are listed in Table 3-4.

Table 3-4. The four types of variables

Name

Member of a Type

Description

Local Variable

No

These hold temporary data within the scope of a method.

Field

Yes

These hold data associated with a type.

Parameter

No

These temporary variables are used to pass data from one method to another method.

Array element

Yes

These are used to store temporary or type-associated data.

Variable Declarations

A variable must be declared before it can be used. The variable declaration defines the variable, and accomplishes two things:

  • It gives the variable a name and associates a type with it.

  • It allows the compiler to allocate memory for it.

A simple variable declaration requires at least a type and a name. The following declaration defines a variable named var2, of type int:

Variable Declarations

For example, Figure 3-11 represents the declaration of four variables and their places on the stack.

Value type and reference type variable declarations

Figure 3-11. Value type and reference type variable declarations

Variable Initializers

Besides declaring a variable's name and type, a declaration can also initialize its memory to a specific value.

A variable initializer consists of an equals sign followed by the initializing value, as shown here:

Variable Initializers

Local variables without initializers have an undefined value, and cannot be used until they have been assigned a value. Attempting to use an undefined local variable causes the compiler to produce an error message.

Figure 3-12 shows a number of local variable declarations on the left, and the resulting stack configuration on the right. Some of the variables have initializers and others do not.

Variable initializers

Figure 3-12. Variable initializers

Automatic Initialization

Some kinds of variables are automatically set to default values if they are declared without an initializer, and others are not. Variables that are not automatically initialized to default values contain undefined values until the program assigns them a value. Table 3-5 shows which types of variables are automatically initialized and which are not. I will cover each of the five variable types later in the text.

Table 3-5. Types of Variables

Variable Type

Stored In

Auto-Initialized

Use

Local variables

Stack or stack and heap

No

Used for local computation inside a function member

Class fields

Heap

Yes

Members of a class

Struct fields

Stack or heap

Yes

Members of a struct

Parameters

Stack

No

Used for passing values into and out of a method

Array elements

Heap

Yes

Members of an array

Multiple-Variable Declarations

You can declare multiple variables in a single declaration statement.

  • The variables in a multiple-variable declaration must all be of the same type.

  • The variable names must be separated with commas. Initializers can be included with the variable names.

For example, the following code shows two valid declaration statements with multiple variables. Notice that the initialized variables can be mixed with uninitialized variables as long as they are separated by commas. The last declaration statement is invalid because it attempts to declare different types of variables in a single statement.

Multiple-Variable Declarations

Using the Value of a Variable

A variable name represents the value stored by the variable. You can use the value by using the variable name.

For example, the value of var2 is retrieved from memory and placed at the position of the variable name, like so:

Console.WriteLine("{0}", var2);
..................Content has been hidden....................

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