Chapter 3. C# Language Fundamentals

Chapter 2 demonstrates a very simple C# program. Nonetheless, there is sufficient complexity in creating even that little program that some of the pertinent details had to be skipped over. The current chapter illuminates these details by delving more deeply into the syntax and structure of the C# language itself.

This chapter discusses the type system in C#, drawing a distinction between built-in types (int, bool, etc.) versus user-defined types (types you create as classes and interfaces). The chapter also covers programming fundamentals such as how to create and use variables and constants. It then goes on to introduce enumerations, strings, identifiers, expressions, and statements.

The second part of the chapter explains and demonstrates the use of branching, using the if, switch, while, do...while, for, and foreach statements. Also discussed are operators, including the assignment, logical, relational, and mathematical operators. This is followed by an introduction to namespaces and a short tutorial on the C# precompiler.

Although C# is principally concerned with the creation and manipulation of objects, it is best to start with the fundamental building blocks: the elements from which objects are created. These include the built-in types that are an intrinsic part of the C# language as well as the syntactic elements of C#.

Types

C# is a strongly typed language. In a strongly typed language you must declare the type of each object you create (e.g., integers, floats, strings, windows, buttons, etc.) and the compiler will help you prevent bugs by enforcing that only data of the right type is assigned to those objects. The type of an object signals to the compiler the size of that object (e.g., int indicates an object of 4 bytes) and its capabilities (e.g., buttons can be drawn, pressed, and so forth).

Like C++ and Java, C# divides types into two sets: intrinsic (built-in) types that the language offers and user-defined types that the programmer defines.

C# also divides the set of types into two other categories: value types and reference types.[5] The principal difference between value and reference types is the manner in which their values are stored in memory. A value type holds its actual value in memory allocated on the stack (or it is allocated as part of a larger reference type object). The address of a reference type variable sits on the stack, but the actual object is stored on the heap.

If you have a very large object, putting it on the heap has many advantages. Chapter 4 discusses the various advantages and disadvantages of working with reference types; the current chapter focuses on the intrinsic value types available in C#.

C# also supports C++ style pointer types, but these are rarely used, and only when working with unmanaged code. Unmanaged code is code created outside of the .NET platform, such as COM objects. Working with COM objects is discussed in Chapter 22.

Working with Built-in Types

The C# language offers the usual cornucopia of intrinsic (built-in) types one expects in a modern language, each of which maps to an underlying type supported by the .NET Common Language Specification (CLS). Mapping the C# primitive types to the underlying .NET type ensures that objects created in C# can be used interchangeably with objects created in any other language compliant with the .NET CLS, such as VB .NET.

Each type has a specific and unchanging size. Unlike with C++, a C# int is always 4 bytes because it maps to an Int32 in the .NET CLS. Table 3-1 lists the built-in value types offered by C#.

Table 3-1. C# built-in value types

Type

Size (in bytes)

.NET Type

Description

byte

1

Byte

Unsigned (values 0-255).

char

1

Char

Unicode characters.

bool

1

Boolean

true or false.

sbyte

1

Sbyte

Signed (values -128 to 127).

short

2

Int16

Signed (short) (values -32,768 to 32,767).

ushort

2

Uint16

Unsigned (short) (values 0 to 65,535).

int

4

Int32

Signed integer values between -2,147,483,647 and 2,147,483,647.

uint

4

Uint32

Unsigned integer values between 0 and 4,294,967,295.

float

4

Single

Floating point number. Holds the values from approximately +/-1.5 * 10-45 to approximate +/-3.4 * 1038 with 7 significant figures.

double

8

Double

Double-precision floating point; holds the values from approximately +/-5.0 * 10-324 to approximate +/-1.7 * 10308 with 15-16 significant figures.

decimal

8

Decimal

Fixed-precision up to 28 digits and the position of the decimal point. This is typically used in financial calculations. Requires the suffix “m” or “M.”

long

8

Int64

Signed integers ranging from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

ulong

8

Uint64

Unsigned integers ranging from 0 to 0xffffffffffffffff.

Tip

C and C++ programmers take note: Boolean variables can only have the values true or false. Integer values do not equate to Boolean values in C# and there is no implicit conversion.

In addition to these primitive types, C# has two other value types: enum (considered later in this chapter) and struct (see Chapter 4). Chapter 4 also discusses other subtleties of value types such as forcing value types to act as reference types through a process known as boxing , and that value types do not “inherit.”

Choosing a built-in type

Typically you decide which size integer to use (short , int, or long ) based on the magnitude of the value you want to store. For example, a ushort can only hold values from 0 through 65,535, while a ulong can hold values from 0 through 4,294,967,295.

That said, memory is fairly cheap, and programmer time is increasingly expensive; most of the time you’ll simply declare your variables to be of type int, unless there is a good reason to do otherwise.

The signed types are the numeric types of choice of most programmers unless they have a good reason to use an unsigned value.

Although you might be tempted to use an unsigned short to double the positive values of a signed short (moving the maximum positive value from 32,767 up to 65,535), it is easier and preferable to use a signed integer (with a maximum value of 2,147,483,647).

It is better to use an unsigned variable when the fact that the value must be positive is an inherent characteristic of the data. For example, if you had a variable to hold a person’s age, you would use an unsigned int because an age cannot be negative.

Float, double, and decimal offer varying degrees of size and precision. For most small fractional numbers, float is fine. Note that the compiler assumes that any number with a decimal point is a double unless you tell it otherwise. To assign a literal float, follow the number with the letter f. (Assigning values to literals is discussed in detail later in this chapter.)

float someFloat = 57f;

The char type represents a Unicode character. char literals can be simple, Unicode, or escape characters enclosed by single quote marks. For example, A is a simple character while u0041 is a Unicode character. Escape characters are special two-character tokens in which the first character is a backslash. For example, is a horizontal tab. The common escape characters are shown in Table 3-2.

Table 3-2. Common escape characters

Char

Meaning

'

Single quote

"

Double quote

\

Backslash


                              

Null

a

Alert



Backspace

f

Form feed


                              

Newline


                              

Carriage return

	

Horizontal tab

v

Vertical tab

Converting built-in types

Objects of one type can be converted into objects of another type either implicitly or explicitly. Implicit conversions happen automatically; the compiler takes care of it for you. Explicit conversions happen when you “cast” a value to a different type. The semantics of an explicit conversion are “Hey! Compiler! I know what I’m doing.” This is sometimes called “hitting it with the big hammer” and can be very useful or very painful, depending on whether your thumb is in the way of the nail.

Implicit conversions happen automatically and are guaranteed not to lose information. For example, you can implicitly cast from a short int (2 bytes) to an int (4 bytes) implicitly. No matter what value is in the short, it will not be lost when converting to an int:

short x = 5;
int y = x; // implicit conversion

If you convert the other way, however, you certainly can lose information. If the value in the int is greater than 32,767 it will be truncated in the conversion. The compiler will not perform an implicit conversion from int to short:

short x;
int y = 500;
x = y;  // won't compile

You must explicitly convert using the cast operator:

short x;
int y = 500;
x = (short) y;  // OK

All of the intrinsic types define their own conversion rules. At times it is convenient to define conversion rules for your user-defined types, as discussed in Chapter 5.



[5] All the intrinsic types are value types except for Object (discussed in Chapter 5) and String (discussed in Chapter 10). All user-defined types are reference types except for structs (discussed in Chapter 7).

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

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