BUY THIS BOOK
Add to Cart

Print Book $49.99


Add to Cart

Print+PDF $64.99

Add to Cart

PDF $39.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £30.99

What is this?

Looking to Reprint or License this content?



Recent Forum Posts
C# 3.0 in a Nutshell
C# 3.0 in a Nutshell, Third Edition A Desktop Quick Reference By Joseph Albahari, Ben Albahari
September 2007
Pages: 858

Cover | Table of Contents | Forum


Table of Contents

Chapter 1: Introducing C# and the .NET Framework
C# is a general-purpose, type-safe, object-oriented programming language. The goal of the language is programmer productivity. To this end, the language balances simplicity, expressiveness, and performance. The chief architect of the language since its first version is Anders Hejlsberg (creator of Turbo Pascal and architect of Delphi). The C# language is platform-neutral, but it was written to work well with the Microsoft .NET Framework.
C# is a rich implementation of the object-orientation paradigm, which includes encapsulation, inheritance, and polymorphism. Encapsulation means creating a boundary around an object, to separate its external (public) behavior from its internal (private) implementation details. The distinctive features of C# from an object-oriented perspective are:
Unified type system
The fundamental building block in C# is an encapsulated unit of data and functions called a type. C# has a unified type system, where all types ultimately share a common base type. This means that all types, whether they represent business objects or are primitive types such as integrals, share the same basic set of functionality. For example, any type can be converted to a string by calling its ToString method.
Classes and interfaces
In the pure objected-oriented paradigm, the only kind of type is a class. In C#, there are several other kinds of types, one of which is an interface (similar to Java interfaces). An interface is like a class except it is only a definition for a type, not an implementation. It's particularly useful in scenarios where multiple inheritance is required (unlike languages such as C++ and Eiffel, C# does not support multiple inheritance of classes).
Properties, methods, and events
In the pure object-oriented paradigm, all functions are methods (this is the case in Smalltalk). In C#, methods are only one kind of
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Object Orientation
C# is a rich implementation of the object-orientation paradigm, which includes encapsulation, inheritance, and polymorphism. Encapsulation means creating a boundary around an object, to separate its external (public) behavior from its internal (private) implementation details. The distinctive features of C# from an object-oriented perspective are:
Unified type system
The fundamental building block in C# is an encapsulated unit of data and functions called a type. C# has a unified type system, where all types ultimately share a common base type. This means that all types, whether they represent business objects or are primitive types such as integrals, share the same basic set of functionality. For example, any type can be converted to a string by calling its ToString method.
Classes and interfaces
In the pure objected-oriented paradigm, the only kind of type is a class. In C#, there are several other kinds of types, one of which is an interface (similar to Java interfaces). An interface is like a class except it is only a definition for a type, not an implementation. It's particularly useful in scenarios where multiple inheritance is required (unlike languages such as C++ and Eiffel, C# does not support multiple inheritance of classes).
Properties, methods, and events
In the pure object-oriented paradigm, all functions are methods (this is the case in Smalltalk). In C#, methods are only one kind of function member, which also includes properties and events (there are others too). Properties are function members that encapsulate a piece of an object's state, such as a button's color or a label's text. Events are function members that simplify acting on object state changes.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Type Safety
C# is primarily a type-safe language, meaning that types can interact only through protocols they define, thereby ensuring each type's internal consistency. For instance, C# prevents you from interacting with a string type as though it were an integer type.
More specifically, C# supports static typing, meaning that the language enforces type safety at compile time. This is in addition to dynamic type safety, which the .NET CLR enforces at runtime.
Static typing eliminates a large class of errors before a program is even run. It shifts the burden away from runtime unit tests onto the compiler to verify that all the types in a program fit together correctly. This makes large programs much easier to manage, more predictable, and more robust. Furthermore, static typing allows tools such as IntelliSense in Visual Studio .NET to help you write a program, since it knows for a given variable what type it is, and hence what methods you can call on that variable.
C# is called a strongly typed language because its type rules (whether enforced statically or dynamically) are very strict. For instance, you cannot call a function that's designed to accept an integer with a floating-point number, unless you first explicitly convert the floating-point number to an integer. This helps prevent mistakes.
Strong typing also plays a role in enabling C# code to run in a sandbox—an environment where every aspect of security is controlled by the host. In a sandbox, it is important that you cannot arbitrarily corrupt the state of an object by bypassing its type rules.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Memory Management
C# relies on the runtime to perform automatic memory management. The CLR has a garbage collector that executes as part of your program, reclaiming memory for objects that are no longer referenced. This frees programmers from explicitly deallocating the memory for an object, eliminating the problem of corrupt pointers, encountered in languages such as C++.
C# does not eliminate pointers: it merely makes them unnecessary for most programming tasks. For performance-critical hotspots and interoperability, pointers may be used, but they are permitted only in blocks that are explicitly marked unsafe.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Platform Support
C# is typically used for writing code that runs on Windows platforms. Although Microsoft standardized the C# language and the CLR through ECMA, the total amount of resources (both inside and outside of Microsoft) dedicated to supporting C# on non-Windows platforms is relatively small. This means that languages such as Java are sensible choices when multiplatform support is the name of the game. Having said this, C# can be used to write cross-platform code in the following scenarios:
  • C# code may run on the server and dish up DHTML that can run on any platform. This is precisely the case for ASP.NET.
  • C# code may run on a runtime other than the Microsoft Common Language Runtime. The most notable example is the Mono project, which has its own C# compiler and runtime, running on Linux, Solaris, Mac OS X, and Windows.
  • C# code may run on a host that supports Microsoft Silverlight (supported for Windows and Mac OS X). This is a new technology that is analogous to Adobe's Flash Player.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
C#'s Relationship with the CLR
C# depends on a runtime equipped with a host of features such as automatic memory management and exception handling. The design of C# closely maps to the design of the CLR, which provides these runtime features (although C# is technically independent of the CLR). Furthermore, the C# type system maps closely to the CLR type system (e.g., both share the same definitions for primitive types).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The CLR and .NET Framework
The .NET Framework consists of a runtime called the Common Language Runtime (CLR) and a vast set of libraries. The libraries consist of core libraries (which this book is concerned with) and applied libraries, which depend on the core libraries. is a visual overview of those libraries (and also serves as a navigational aid to the book).
Figure 1-1: This depicts the topics covered in this book and the chapters in which they are found. The names of specialized frameworks and class libraries beyond the scope of this book are grayed out and displayed outside the boundaries of The Nutshell.
The CLR is the runtime for executing managed code. C# is one of several managed languages that get compiled into managed code. Managed code is packaged into an assembly, in the form of either an executable file (an .exe) or a library (a .dll), along with type information, or metadata.
Managed code is represented in Intermediate Language or IL. When the CLR loads an assembly, it converts the IL into the native code of the machine, such as x86. This conversion is done by the CLR's JIT (Just-In-Time) compiler. An assembly retains almost all of the original source language constructs, which makes it easy to inspect and even generate code dynamically.
Lutz Roeder's .NET Reflector application is an invaluable tool for examining the contents of an assembly (you can also use it as a decompiler).
The CLR performs as a host for numerous runtime services. Examples of these services include memory management, the loading of libraries, and security services.
The CLR is language-neutral, allowing developers to build applications in multiple languages (e.g., C#, Visual Basic .NET, Managed C++, Delphi.NET, Chrome .NET, and J#).
The .NET Framework consists of libraries for writing just about any Windows-based application. gives an overview of the .NET Framework libraries.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What's New in C# 3.0
C# 3.0 features are centered on Language Integrated Query capabilities, or LINQ for short. LINQ was inspired by the work done on Comega (formerly known as X# and Xen). Erik Meijer was the primary architect who worked closely with Anders Hejlsberg to incorporate that work into C#.
LINQ enables SQL-like queries to be written directly within a C# program and checked statically for correctness. The architecture of LINQ allows queries to execute either locally or remotely. The .NET Framework provides LINQ-enabled APIs across local collections, remote databases, and XML. C# 3.0 features include:
  • Lambda expressions
  • Extension methods
  • Implicitly typed local variables
  • Query comprehensions
  • Anonymous types
  • Object initializers
  • Implicitly typed arrays
  • Automatic properties
  • Partial methods
  • Expression trees
Lambda expressions are like miniature functions created on the fly. They are a natural evolution of anonymous methods introduced in C# 2.0, and in fact, completely subsume the functionality of anonymous methods. For example, the following lambda expression squares an integer:
Func<int,int> square =x => x * x;
Console.WriteLine (square(3));         // 9
For people familiar with functional programming languages, such as Scheme and Haskell, lambda expressions will be a familiar construct. The primary use case in C# is with LINQ queries, such as the following:
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames =                // Include only names
  Enumerable.Where (names, n =>n.Length >= 4);    // of >= 4 characters.
Extension methods extend an existing type with new methods, without altering the type's definition. They act as syntactic sugar, making static methods feel like instance methods. Because LINQ's query operators are implemented as extension methods, we can simplify our preceding query as follows:
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = names.Where (n => n.Length >= 4);
Implicitly typed local variables
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: C# Language Basics
In this chapter, we introduce the basics of the C# language.
Here is a program that multiplies 12 by 30, and prints the result, 360, to the screen. The double-forward slash indicates that the remainder of a line is a comment.
using System;                     // importing namespace

class Test                        // class declaration
{
  static void Main (  )             //   method declaration
  {
    int x = 12 * 30;              //     statement 1
    Console.WriteLine (x);        //     statement 2
  }                               //   end of method
}                                 // end of class
At the heart of this program lies two statements. Statements in C# execute sequentially. Each statement is terminated by a semicolon:
    int x = 12 * 30;
    Console.WriteLine (x);
The first statement computes the expression 12 * 30 and stores the result in a local variable, named x, which is an integer type. The second statement calls the Console class's WriteLine method, to print the variable x to a text window on the screen.
A method performs an action in a series of statements, called a statement block —a pair of braces containing zero or more statements. We defined a single method named Main:
  static void Main (  )
  {
    ...
  }
Writing higher-level functions that call upon lower-level functions simplifies a program. We can refactor our program with a reusable method that multiplies an integer by 12 as follows:
using System;

class Test
{
  static void Main (  )
  {
    Console.WriteLine (FeetToInches (30));      // 360
    Console.WriteLine (FeetToInches (100));     // 1200
  }

  static int FeetToInches (int feet)
  {
    int inches = feet * 12;
    return inches;
  }
}
A method can receive input data from the caller by specifying parameters and output data back to the caller by specifying a return type. We defined a method called FeetToInches that has a parameter for inputting feet, and a return type for outputting inches:
staticint InchesToFeet (int feet
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
A First C# Program
Here is a program that multiplies 12 by 30, and prints the result, 360, to the screen. The double-forward slash indicates that the remainder of a line is a comment.
using System;                     // importing namespace

class Test                        // class declaration
{
  static void Main (  )             //   method declaration
  {
    int x = 12 * 30;              //     statement 1
    Console.WriteLine (x);        //     statement 2
  }                               //   end of method
}                                 // end of class
At the heart of this program lies two statements. Statements in C# execute sequentially. Each statement is terminated by a semicolon:
    int x = 12 * 30;
    Console.WriteLine (x);
The first statement computes the expression 12 * 30 and stores the result in a local variable, named x, which is an integer type. The second statement calls the Console class's WriteLine method, to print the variable x to a text window on the screen.
A method performs an action in a series of statements, called a statement block —a pair of braces containing zero or more statements. We defined a single method named Main:
  static void Main (  )
  {
    ...
  }
Writing higher-level functions that call upon lower-level functions simplifies a program. We can refactor our program with a reusable method that multiplies an integer by 12 as follows:
using System;

class Test
{
  static void Main (  )
  {
    Console.WriteLine (FeetToInches (30));      // 360
    Console.WriteLine (FeetToInches (100));     // 1200
  }

  static int FeetToInches (int feet)
  {
    int inches = feet * 12;
    return inches;
  }
}
A method can receive input data from the caller by specifying parameters and output data back to the caller by specifying a return type. We defined a method called FeetToInches that has a parameter for inputting feet, and a return type for outputting inches:
staticint InchesToFeet (int feet ) {...}
The literals 30 and 100 are the arguments passed to the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Syntax
C# syntax is based on C and C++ syntax. In this section, we will describe C#'s elements of syntax, using the following program:
using System;

class Test
{
  static void Main (  )
  {
    int x = 12 * 30;
    Console.WriteLine (x);
  }
}
Identifiers are names that programmers choose for their classes, methods, variables, and so on. These are the identifiers in our example program, in the order they appear:
System   Test   Main   x   Console   WriteLine
An identifier must be a whole word, essentially made up of Unicode characters starting with a letter or underscore. C# identifiers are case-sensitive. By convention, arguments, local variables, and private fields should be in camel case (e.g., myVariable ), and all other identifiers should be in Pascal case (e.g., MyMethod ).
Keywords are names reserved by the compiler that you can't use as identifiers. These are the keywords in our example program:
using   class   static   void   int
Here is the full list of C# keywords:
abstract
As
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal
default
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for
foreach
goto
if
implicit
in
int
interface
internal
is
lock
long
namespace
new
null
object
operator
out
override
params
private
protected
public
readonly
ref
return
sbyte
sealed
short
sizeof
stackalloc
static
string
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
ushort
using
virtual
void
while

Section 2.2.1.1: Avoiding conflicts

If you really want to use an identifier that clashes with a keyword, you can do so by qualifying it with the @ prefix. For instance:
class class  {...}      // illegal
class @class {...}      // legal
The @ symbol doesn't form part of the identifier itself. So @myVariable is the same as myVariable.

Section 2.2.1.2: Contextual keywords

The language also has contextual keywords, which, while recognized by the compiler, can still be used, unqualified, as identifiers. These are:
add
ascending
by
descending
equals
from
get
global
group
in
into
join
let
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Type Basics
A type defines the blueprint for a value. A value is a storage location denoted by a variable or a constant. A variable represents a value that can change, whereas a constant represents an invariant (we will visit constants later in the chapter). We created a local variable named x in our first program:
static void Main (  )
{
  int x = 12 * 30;
  Console.WriteLine (x);
}
All values in C# are an instance of a specific type. The meaning of a value, and the set of possible values a variable can have, is determined by its type. The type of x is int.
Predefined types are types that are specially supported by the compiler. The int type is a predefined primitive type for representing the set of integers that fit into 32 bits of memory, from −231 to 231−1. We can perform functions such as arithmetic with instances of the int type as follows:
int x = 12 * 30;
Another predefined C# type is the string type. The string type represents a sequence of characters, such as ".NET" or "http://oreilly.com". We can manipulate strings by calling functions on them as follows:
string message = "Hello world";
string upperMessage = message.ToUpper(  );
Console.WriteLine (upperMessage);               // HELLO WORLD

int x = 2007;
message = message + x.ToString(  );
Console.WriteLine (message);                    // Hello world2007
The primitive bool type has exactly two possible values: true and false. The bool type is commonly used to conditionally branch execution flow based with an if statement. For example:
bool simpleVar = false;
if (simpleVar)
  Console.WriteLine ("This will not print");

int x = 5000;
bool lessThanAMile = x < 5280;
if (lessThanAMile)
  Console.WriteLine ("This will print");
In C#, predefined types (also referred to as built-in types) are recognized with a C# keyword. The System namespace in the .NET Framework contains many important types that are not predefined by C# (e.g., DateTime ).
Just as we can build complex functions from simple functions, we can build complex types from primitive types. In this example, we will define a custom type named
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Numeric Types
C# has the predefined numeric types shown in .
Table 2-1: Predefined numeric types in C#
C# type
System type
Suffix
Category
Size
Range
Notes
sbyte
SByte
Integral
8 bits
−27 to 27−1
short
Int16
Integral
16 bits
−215 to 215−1
int
Int32
Integral
32 bits
-231 to 231−1
long
Int64
L
Integral
64 bits
−263 to 263−1
byte
Byte
Integral
8 bits
0 to 28−1
Unsigned
ushort
UInt16
Integral
16 bits
0 to 216−1
Unsigned
uint
UInt32
U
Integral
32 bits
0 to 232−1
Unsigned
ulong
UInt64
UL
Integral
64 bits
0 to 264−1
Unsigned
float
Single
F
Real
32 bits
±(∼10−45 to ∼1038)
Single-precision
double
Double
D
Real
64 bits
±(∼10−324 to ∼10308)
Double-precision
decimal
Decimal
M
Real
128 bits
±(∼10−28 to ∼1028)
Base 10
Of the integral types, int and long are first-class citizens and are favored by both C# and the runtime. The other integral types are typically used for interoperability or when space efficiency is paramount.
Of the real number types, float and double are called floating-point types and are typically used for scientific calculations. The decimal type is typically used for financial calculations, where base-10-accurate arithmetic and high precision are required.
Integral literals can use decimal or hexadecimal notation; hexadecimal is denoted with the 0x prefix. For example:
int x = 127;
long y = 0x7F;
Real literals can use decimal and/or exponential notation. For example:
double d = 1.5;
double million = 1E06;

Section 2.4.1.1: Numeric literal type inference

By default, the compiler infers a numeric literal to be either double or an integral type:
  • If the literal contains a decimal point or the exponential symbol (E), it is a double.
  • Otherwise, the literal's type is the first type in this list that can fit the literal's value: int, uint, ulong, and long.
For example:
Console.WriteLine (        1.0.GetType(  ));  // Double  (double)
Console.WriteLine (       1E06.GetType(  ));  // Double  (double)
Console.WriteLine (          1.GetType(  ));  // Int32   (int)
Console.WriteLine ( 0xF0000000.GetType(  ));  // UInt32  (uint)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Boolean Type and Operators
C#'s bool type (aliasing the System.Boolean type) is a logical value that can be assigned the literal true or false.
Although a boolean value requires only one bit (0 or 1) of storage, the runtime will use much more storage since this is the minimum chunk with which most lower-level parts of the system (from the runtime down to the processor) can efficiently work. For example, each element in a bool array uses two bytes of memory. The System.Collections.BitArray class can be used where the storage efficiency of one bit per boolean value is required.
No conversions can be made from the bool type to numeric types or vice versa.
== and != test for equality and inequality of any type, but always return a bool value. Value types typically have a very simple notion of equality:
int x = 1;
int y = 2;
int z = 1;
Console.WriteLine (x == y);         // False
Console.WriteLine (x == z);         // True
For reference types, equality, by default, is based on reference, as opposed to the actual value of the underlying object:
public class Dude
{
  public string Name;
  public Dude (string n) { Name = n; }
}

Dude d1 = new Dude ("John");
Dude d2 = new Dude ("John");
Console.WriteLine (d1 == d2);       // False
Dude d3 = d1;
Console.WriteLine (d1 == d3);       // True
The comparison operators, <, >, >=, and <=, work for all numeric types, but should be used with caution with real numbers (see the "" section earlier in this chapter). The comparison operators also work on enum type members, by comparing their underlying integral values. This is described in the "" section in .
We explain the equality and comparison operators in greater detail in the section "" in , and in the sections "" and "" in .
The && and || operators test for and and or conditions. They are frequently used in conjunction with the ! operator, which expresses not. In this example, the UseUmbrella method returns true if it's rainy or sunny (to protect us from the rain or the sun), as long as it's not also windy (since umbrellas are useless in the wind):
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Strings and Characters
C#'s char type (aliasing the System.Char type) represents a Unicode character and occupies two bytes. A char literal is specified inside single quotes:
char c = 'A';       // simple character
Escape sequences express characters that cannot be expressed or interpreted literally. An escape sequence is a backslash followed by a character with a special meaning. For example:
char newLine = '\n';
char backSlash = '\\';
lists the escape sequence characters.
Table 2-6: Character and string escape sequences
Char
Meaning
Value
\'
Single quote
0x0027
\"
Double quote
0x0022
\\
Backslash
0x005C
\0
Null
0x0000
\a
Alert
0x0007
\b
Backspace
0x0008
\f
Form feed
0x000C
\n
New line
0x000A
\r
Carriage return
0x000D
\t
Horizontal tab
0x0009
\v
Vertical tab
0x000B
The \u (or \x) escape sequence lets you specify any Unicode character via its four-digit hexadecimal code.
char copyrightSymbol = '\u00A9';
char omegaSymbol     = '\u03A9';
char newLine         = '\u000A';
An implicit conversion from a char to a numeric type works for the numeric types that can accommodate an unsigned short. For other numeric types, an explicit conversion is required.
C#'s string type (aliasing the System.String type, covered in depth in ) represents an immutable sequence of Unicode characters. A string literal is specified inside double quotes:
string a = "Heat";
string is a reference type, rather than a value type. However, since a string is immutable, it takes on value-like semantics.
The escape sequences that are valid for char literals also work inside strings:
string a = "Blah blah.\n";
The cost of this is that whenever you need a literal backslash, you must write it twice:
string a1 = "\\\\server\\fileshare\\helloworld.cs";
To avoid this problem, C# allows verbatim string literals. A verbatim string literal is prefixed with @ and does not support escape sequences. The following verbatim string is identical to the preceding one:
string a2 =@ "\\server\fileshare\helloworld.cs";
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Arrays
An array represents a fixed number of elements of a particular type. The elements in an array are always stored in a contiguous block of memory, providing highly efficient access.
An array is denoted with square brackets after the element type. For example:
char[] vowels = new char[5];    // Declare an array of 5 characters
Square brackets also index the array, accessing a particular element by position:
vowels [0] = 'a';
vowels [1] = 'e';
vowels [2] = 'i';
vowels [3] = 'o';
vowels [4] = 'u';
Console.WriteLine (vowels [1]);      // e
This prints "e" because array indexes start at 0. We can use a for loop statement to iterate through each element in the array. The for loop in this example cycles the integer i from 0 to 4:
for (int i = 0; i < vowels.Length; i++)
  Console.Write (vowels [i]);            // aeiou
The Length property of an array returns the number of elements in the array. Once an array has been created, its length cannot be changed. The System.Collection namespace and subnamespaces provide higher-level data structures, such as dynamically sized arrays and dictionaries.
An array initialization expression specifies each element of an array. For example:
char[] vowels = new char[] {'a','e','i','o','u'};
All arrays inherit from the System.Array class, providing common services for all arrays. These members include methods to get and set elements regardless of the array type, and are described in the section "" in .
Creating an array always preinitializes the elements with default values. The default value for a type is the result of a bitwise zeroing of memory. For example, consider creating an array of integers. Since int is a value type, this allocates 1,000 integers in one contiguous block of memory. The default value for each element will be 0:
int[] a = new int[1000];
Console.Write (a[123]);            // 0

Section 2.7.1.1: Value types versus reference types

Whether an array element type is a value type or a reference type has important performance implications. When the element type is a value type, each element value is allocated as part of the array. For example:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Variables and Parameters
A variable represents a storage location that has a modifiable value. A variable can be a local variable, parameter (value, ref, or out), field (instance or static), or array element.
The stack and the heap are the places where variables and constants reside. Each has very different lifetime semantics.

Section 2.8.1.1: Stack

The stack is a block of memory for storing local variables and parameters. The stack automatically grows and shrinks as a function is entered and exited. Consider the following method (to avoid distraction, input argument checking is ignored):
static int Factorial (int x)
{
  if (x == 0) return 1;
  return x * Factorial (x-1);
}
This method is recursive, meaning that it calls itself. Each time the method is entered, a new int is allocated on the stack, and each time the method exits, the int is deallocated.

Section 2.8.1.2: Heap

The heap is a block of memory in which objects (i.e., reference type instances) reside. Whenever a new object is created, it is allocated on the heap, and a reference to that object is returned. During a program's execution, the heap starts filling up as new objects are created. The runtime has a garbage collector that periodically deallocates objects from the heap, so your computer does not run out of memory. An object is eligible for deallocation as soon as nothing references it.
In the following example, we create two StringBuilder objects, referenced by the variables ref1 and ref2. The variable ref3 is then assigned to the object referenced by ref2. Next, we assign both ref1 and ref2 to null. At this point, object1 (i.e., the first StringBuilder object created) is eligible for garbage collection, since nothing references it. In contrast, object2 (i.e., the second StringBuilder object created) is still referenced by ref3, so it is not eligible for garbage collection. When the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Expressions and Operators
An expression essentially denotes a value. The simplest kinds of expressions are constants and variables. Expressions can be transformed and combined using operators. An operator takes one or more input operands to output a new expression.
Here is an example of a constant expression:
12
We can use the * operator to combine two operands (the literal expressions 12 and 30), as follows:
12 * 30
Complex expressions can be built because an operand may itself be an expression, such as the operand (12 * 30) in the following example:
1 + (12 * 30)
Operators in C# are classed as unary, binary, or ternary—depending on the number of operands they work on (one, two, or three). The binary operators always use infix notation, where the operator is placed between the two operands.
Primary expressions include expressions composed of operators that are intrinsic to the basic plumbing of the language. Here is an example:
Math.Log (1)
This expression is composed of two primary expressions. The first expression performs a member-lookup (with the . operator), and the second expression performs a method call (with the ( ) operator).
A void expression is an expression that has no value. For example:
Console.WriteLine (1)
A void expression, since it has no value, cannot be used as an operand to build more complex expressions:
1 + Console.WriteLine(1)      // Compile-time error
An assignment expression uses the = operator to assign the result of another expression to a variable. For example:
x = x * 5
An assignment expression is not a void expression. It actually carries the assignment value, and so can be incorporated into another expression. In the following example, the expression assigns 2 to x and 10 to y:
y = 5 * (x = 2)
This style of expression can be used to initialize multiple values:
a = b = c = d = 0
The compound assignment operators are syntactic shortcuts that combine assignment with another operator. For example:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Statements
Functions comprise statements that execute sequentially in the textual order in which they appear. A statement block is a series of statements appearing between braces (the {} tokens).
A declaration statement declares a new variable, optionally initializing the variable with an expression. A declaration statement ends in a semicolon. You may declare multiple variables of the same type in a comma-separated list. For example:
string someWord = "rosebud";
int someNumber = 42;
bool rich = true, famous = false;
A constant declaration is like a variable declaration, except that the variable cannot be changed after it has been declared, and the initialization must occur with the declaration:
const double c = 2.99792458E08;
c+=10; // error

Section 2.10.1.1: Local variables

The scope of a local or constant variable extends to the end of the current block. You cannot declare another local variable with the same name in the current block or in any nested blocks. For example:
static void Main(  )
{
  int x;
  {
    int y;
    int x;              // error, x already defined
  }
  {
    int y;              // ok, y not in scope
  }
  Console.WriteLine(y); // error, y is out of scope
}
Expression statements are expressions that are also valid statements. An expression statement must either change state or call something that might change state. Changing state essentially means changing a variable. The possible expression statements are:
  • Assignment expressions (including increment and decrement expressions)
  • Method call expressions (both void and nonvoid)
  • Object instantiation expressions
Here are some examples:
// declare variables with declaration statements:
string s;
int x, y;
System.Text.StringBuilder sb;

// expression statements
x = 1 + 2;                 // assignment expression
x++;                       // increment expression
y = Math.Max(x, 5);        // assignment expression
Console.WriteLine(y);      // method call expression
sb = new StringBuilder(  );  // assignment expression
new StringBuilder(  );       // object instantiation expression
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Namespaces
Content preview·Buy PDF of this chapter|