Chapter 2 demonstrated a very simple C# program. Nonetheless, that little program was complex enough that we had to skip some of the pertinent details. This chapter illuminates these details by delving more deeply into the syntax and structure of the C# language itself.
In this chapter, I discuss the type system in C#, covering built-in types such as int
and bool
, and user-defined types (types you create) such as classes, structs, and interfaces. I also cover programming fundamentals, such as how to create and use variables and constants. I’ll then introduce enumerations, strings, identifiers, expressions, and statements.
In the second part of the chapter, I’ll explain and demonstrate the use of flow control statements, using the if, switch, while, do...while, for
, and foreach
statements. You’ll also learn about operators, including the assignment, logical, relational, and mathematical operators. I’ll finish up with a short tutorial on the C# preprocessor.
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#.
Every variable and object in C# has a “type.” There are built-in types (e.g., int
), and you may create your own types (e.g., Employee
).
When you create an object, you declare its type, and in a statically typed language such as C#, the compiler will “enforce” that typing, giving you an error at compile time (rather than runtime) if you violate the typing by (for example) trying to assign an employee object to an integer variable. This is a good thing; it cuts down on bugs and makes for more reliable code.
In the vast majority of cases, C# is also “manifestly” typed—which means that you explicitly declare the type of the object. There is one exception, which is the use of the keyword var
(covered in Chapter 13). In this case, C# is able to infer the type of the object and thus, rather than being manifest, is actually implicit.
Finally, C# is strongly typed, which means that any operation you attempt on any object or variable must be appropriate to that type, or it will cause a compiler error. Once again, this is a good thing; it helps identify bugs reliably at compile time.
In summary, we can say that C# is statically, manifestly, and strongly typed when using most types, except when using the keyword var
, at which time it is statically, implicitly, and strongly typed!
Key to all of this is that it is always statically and strongly typed, which means that you must declare your types, and the compiler will then enforce that you use your objects according to their declared types, and this is a good thing.
The C# language itself offers the usual cornucopia of intrinsic (built-in) types you expect in a modern language, each of which maps to an underlying type supported by the .NET CTS. Mapping the C# primitive types to the underlying .NET types ensures that objects you create in C# can be used interchangeably with objects created in any other language compliant with the .NET CTS, such as Visual Basic.
Each built-in type has a specific and unchanging size. Table 3-1 lists many of the built-in types offered by C#.
Type | Size (in bytes) | .NET type | Description |
| 1 |
| Unsigned (values 0 to 255) |
| 2 |
| Unicode characters |
| 1 |
| True or false |
| 1 |
| Signed values (−128 to 127) |
| 2 |
| Signed short values (−32,768 to 32,767) |
| 2 |
| Unsigned short values (0 to 65,535) |
| 4 |
| Signed integer values between −2,147,483,648 and 2,147,483,647 |
| 4 |
| Unsigned integer values between 0 and 4,294,967,295 |
| 4 |
| Floating-point number. Holds values from approximately +/−1.5 × 10−45 to approximately +/−3.4 × 1038 with seven significant figures |
| 8 |
| Double-precision floating-point. Holds values from approximately +/−5.0 × 10−324 to approximately +/−1.8 × 10308 with 15 to 16 significant figures |
| 16 |
| Fixed-precision value up to 28 digits and the position of the decimal point; this is typically used in financial calculations; requires the suffix “m” or “M” |
| 8 |
| Signed integers from −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
| 8 |
| Unsigned integers ranging from 0 to 0xffffffffffffffff |
In addition to these primitive types, C# has two other value types: enum
(which I’ll explain later in this chapter) and struct
(discussed in Chapter 4).
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 to 65,535, whereas a uint
can hold values from 0 to 4,294,967,295.
With that said, memory is fairly cheap, and programmer time is increasingly expensive; so, most of the time you’ll simply declare your variables to be of type int
, unless there is a good reason to do otherwise.
When you need to create numbers that represent noninteger values (e.g., 5.7), you’ll choose among float, double
, and decimal
, depending on the size and degree of precision you need. For most small fractional numbers, float
is fine.
The compiler assumes that any number with a decimal point is a double unless you tell it otherwise. You must therefore use the f
suffix for a float, and the m
for a decimal, but no other suffixes are required for other types.
To create a float
, follow the number with the letter f
:
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, whereas u0041
is a Unicode character. Escape characters are special two-character tokens that have special meaning to the compiler in which the first character is a backslash. For example,
is a horizontal tab. Table 3-2 shows the common escape characters.