for RuBoard |
In C# there is a fundamental distinction between value types and reference types. Value types have storage allocated immediately on the stack when the variable is declared. Reference types have storage allocated on the heap, and the variable is only a reference to the actual data, which can be allocated later.
We have been looking at classes in some detail. A class defines a reference type. In this section we survey the entire C# type system, including simple types such as int and decimal . In C# a struct has many similarities to a class but is a value type. Another important kind of value type in C# is an enum .
We examine later several other important types, including string, array, interface, and delegate. We will discuss the default values that get assigned to variables when there is not an explicit initialization. We will see that all types in C# are rooted in a fundamental base class called object . In C# "everything is an object," and value types are transparently converted to object references as needed through a process known as boxing . The inverse process, unboxing , returns an object to the value type from which it came.
In C# there are three kinds of types:
Value types
Reference types
Pointer types
Value types directly contain their data. Each variable of a value type has its own copy of the data. Value types typically are allocated on the stack and are automatically destroyed when the variable goes out of scope. Value types include the simple types like int and decimal , structures, and enumeration types.
Reference types do not contain data directly but only refer to data. Variables of reference types store references to data, called objects. Two different variables can reference the same object. Reference types are allocated on the managed heap and eventually get destroyed through a process known as garbage collection .
Reference types include string , object , class types, array types, interfaces, and delegates.
Pointer types are used only in unsafe code and will be discussed later in this chapter.
In this section we survey all the value types, including the simple types, structures, and enumerations.
The simple data types are general-purpose value data types, including numeric, character, and Boolean.
The sbyte data type is an 8-bit signed integer.
The byte data type is an 8-bit unsigned integer.
The short data type is a 16-bit signed integer.
The ushort data type is a 16-bit unsigned integer.
The int data type is a 32-bit signed integer.
The uint data type is a 32-bit unsigned integer.
The long data type is a 64-bit signed integer.
The ulong data type is a 64-bit unsigned integer.
The char data type is a Unicode character (16 bits).
The float data type is a single-precision floating point.
The double data type is a double-precision floating point.
The bool data type is a Boolean ( true or false ).
The decimal data type is a decimal type with 28 significant digits (typically used for financial purposes).
There is an exact correspondence between the simple C# types and types in the System namespace. C# reserved words are simply aliases for the corresponding types in the System namespace. Table 3-3 shows this correspondence.
C# Reserved Word | Type in System Namespace |
---|---|
sbyte | System.SByte |
byte | System.Byte |
short | System.Int16 |
ushort | System.UInt16 |
int | System.Int32 |
uint | System.UInt32 |
long | System.Int64 |
ulong | System.UInt64 |
char | System.Char |
float | System.Single |
double | System.Double |
bool | System.Boolean |
decimal | System.Decimal |
A struct is a value type which can group heterogeneous types together. It can also have constructors and methods . In C++ the concept of class and struct is very close. In C++ a class has default visibility of private and a struct has default visibility of public , and that is the only difference. There is a more fundamental difference in C#.
In C# the key difference between a class and a struct is that a class is a reference type and a struct a value type. A class must be instantiated explicitly using new . The new instance is created on the heap, and memory is managed by the system through a garbage-collection process. Since a default constructor will be created for a struct if none is defined, a struct declared on the stack will be initialized . You may also use new . A new instance of a struct is created on the stack, and the instance will be deallocated when it goes out of scope.
There are different semantics for assignment, whether done explicitly or via call by value mechanism in a method call. For a class, you will get a second object reference, and both object references refer to the same data. For a struct, you will get a completely independent copy of the data in the struct.
A struct is a convenient data structure to use for moving data across a process or machine boundary, and we will use structs in our case study. For example, we will use a struct to represent customer data.
public struct CustomerListItem { public int CustomerId; public string FirstName; public string LastName; public string EmailAddress; }
The final kind of value type is an enumeration type. An enumeration type is a distinct type with named constants. Every enumeration type has an underlying type, which is one of the following.
byte
short
int
long
An enumeration type is defined through an enum declaration.
public enum BookingStatus : byte { HotelNotFound, // 0 implicitly RoomsNotAvailable, // 1 implicitly Ok = 5 // explicit value }
If the type is not specified, int is used. By default, the first enum member is assigned the value 0, the second member 1, and so on. Constant values can be explicitly assigned.
You can make use of an enumeration type by declaring a variable of the type indicated in the enum declaration (e.g., BookingStatus ). You can refer to the enumerated values by using the dot notation. Here is some illustrative code:
BookingStatus status; status = hotel.ReserveRoom(name, date); if (status == BookingStatus.HotelNotFound) Console.WriteLine("Hotel not found"); ...
A variable of a reference type does not directly contain its data but instead provides a reference to the data stored in the heap. In C# there are the following kinds of reference types:
Class
Array
Interface
Delegate
Reference types have a special value null , which indicates the absence of an instance.
We have already examined classes in some detail, and we will look at arrays later in this chapter. Interfaces and delegates will be covered in Chapter 5.
A class type defines a data structure that has fields, methods, constants, and other kinds of members . Class types support inheritance . Through inheritance a derived class can extend or specialize a base class. We will discuss inheritance in Chapter 4.
Two classes in the .NET Framework Class Library are so important that they have C# reserved words as aliases for them: object and string .
The object class type is the ultimate base type for all types in C#. Every C# type derives directly or indirectly from object . The object keyword in C# is an alias for the predefined System.Object class. System.Object has methods such as ToString , Equals , and Finalize , which we will study later.
The string class encapsulates a Unicode character string. The string keyword is an alias for the predefined System.String class. The string type is a sealed class. (A sealed class is one that cannot be used as the base class for any other classes.)
The string class inherits directly from the root object class. String literals are defined using double quotes. There are useful built-in methods for string . For now, note that the Equals method can be used to test for equality of strings.
string a = "hello"; if (a.Equals("hello")) Console.WriteLine("equal"); else Console.WriteLine("not equal");
There are also overloaded operators:
if (a == "hello") ...
We will study string in detail later in this chapter.
Several kinds of variables are automatically initialized to default values:
Static variables
Instance variables of class and struct instances
Array elements
Local variables are not automatically initialized, and you will get a compiler error message if you try to use a local variable that has not been initialized.
The default value of a variable of reference type is null .
The default value of a variable of value type is the value assigned in the default constructor. For simple types this value corresponds to a bit pattern of all zeros:
For integer types, the default value is 0
For char , the default value is '\u0000'
For float , the default value is 0.0f
For double , the default value is 0.0d
For decimal , the default value is 0.0m
For bool , the default value is false
For an enum type, the default value is 0. For a struct type, the default value is obtained by setting all value type fields to their default values, as described above, and all reference type fields to null .
One of the strong features of C# is that is has a unified type system. Every type, including the simple built-in types such as int , derive from System.Object . In C# "everything is an object."
A language such as Smalltalk also has such a feature but pays the price of inefficiency for simple types. Languages such as C++ and Java treat simple built-in types differently from objects, thus obtaining efficiency but at the cost of a unified type system.
C# enjoys the best of both worlds through a process known as boxing . Boxing converts a value type such as int or a struct to an object reference and is done implicitly. Unboxing converts a boxed value type (stored on the heap) back to an unboxed simple value (stored on the stack). Unboxing is done through a type cast.
int x = 5; object o = x; // boxing x = (int) o; // unboxing
for RuBoard |