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.
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
.
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.
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.
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.
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:
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.
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 | Default Value | |
---|---|---|---|---|
| 8-bit unsigned integer | −128–127 |
|
|
| 8-bit unsigned integer | 0–255 |
|
|
| 16-bit unsigned integer | −32,768–32,767 |
|
|
| 16-bit unsigned integer | 0–65,535 |
|
|
| 32-bit signed integer | −2,147,483,648–2,147,483,647 |
|
|
| 32-bit unsigned integer | 0–4,294,967,295 |
| 0 |
| 64-bit signed integer | −9,223,372,036,854,775,808–9,223,372,036,854,775,807 |
|
|
| 64-bit unsigned integer | 0–18,446,744,073,709,551,615 |
|
|
| Single-precision float | 1.5×10−45–3.4×1038 |
|
|
| Double-precision float | 5×10−324–1.7×10308 |
|
|
| Boolean |
|
|
|
| Unicode character | U+0000–U+ffff |
|
|
| Decimal value with 28-significant-digit precision | ±1.0×1028–±7.9×1028 |
|
|
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.
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.
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 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
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.
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.
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.
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.
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.
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.
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.
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
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. |
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
:
For example, Figure 3-11 represents the declaration of four variables and their places on the stack.
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:
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.
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 |
Struct fields | Stack or heap | Yes | Members of a |
Parameters | Stack | No | Used for passing values into and out of a method |
Array elements | Heap | Yes | Members of an array |
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.
3.139.82.4