C# 3.0 Pocket Reference
C# 3.0 Pocket Reference, Second Edition Instant Help for C# 3.0 Programmers

By Joseph Albahari, Ben Albahari
Book Price: $14.99 USD
£8.99 GBP
PDF Price: $11.99

Cover | Table of Contents


Table of Contents

Chapter 1: C# 3.0 Pocket Reference
C# is a general-purpose, type-safe, object-oriented programming language whose goal is programmer productivity. To this end, the language balances simplicity, expressiveness, and performance. The C# language is platform-neutral, but it was written to work well with the Microsoft .NET Framework. C# 3.0 targets .NET Framework 3.5.
C# 3.0 features are centered on Language Integrated Query capabilities, or LINQ for short. LINQ enables SQL-like queries to be written directly within a C# program, and checked statically for correctness. Queries can 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
  • Implicitly typed arrays
  • Object initializers
  • 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:
	Func<int,int> sqr = x => x * x;
	Console.WriteLine (sqr(3));           // 9
The primary use case in C# is with LINQ queries, such as the following:
	string[] names = { "Tom", "Dick", "Harry" };

	// Include only names of >= 4 characters:

	IEnumerable<string> filteredNames =
	  Enumerable.Where (names, n => n.Length >= 4);
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:
	IEnumerable<string> filteredNames =
	  names.Where (n => n.Length >= 4);
Implicitly typed local variables let you omit the variable type in a declaration statement, allowing the compiler to infer it. Because the compiler can determine the type 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!
What's New in C# 3.0
C# 3.0 features are centered on Language Integrated Query capabilities, or LINQ for short. LINQ enables SQL-like queries to be written directly within a C# program, and checked statically for correctness. Queries can 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
  • Implicitly typed arrays
  • Object initializers
  • 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:
	Func<int,int> sqr = x => x * x;
	Console.WriteLine (sqr(3));           // 9
The primary use case in C# is with LINQ queries, such as the following:
	string[] names = { "Tom", "Dick", "Harry" };

	// Include only names of >= 4 characters:

	IEnumerable<string> filteredNames =
	  Enumerable.Where (names, n => n.Length >= 4);
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:
	IEnumerable<string> filteredNames =
	  names.Where (n => n.Length >= 4);
Implicitly typed local variables let you omit the variable type in a declaration statement, allowing the compiler to infer it. Because the compiler can determine the type of filteredNames, we can further simplify our query:
	var filteredNames = names.Where (n => n.Length == 4);
Query comprehension syntax provides SQL-style syntax for writing queries. Comprehension syntax can simplify certain kinds of queries substantially, as well as serving as syntactic sugar for lambda-style queries. Here's the previous example in comprehension syntax:
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 x 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 lie 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:
	static int InchesToFeet (int feet) {...} 
The literals 30 and 100 are the arguments passed to the FeetToInches method. 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 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 in which 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
enum
long
stackalloc
as
event
namespace
static
base
explicit
new
string
bool
extern
null
struct
break
false
object
switch
byte
finally
operator
this
case
fixed
out
throw
catch
float
override
true
char
for
params
try
checked
foreach
private
typeof
class
goto
protected
uint
const
if
public
ulong
continue
implicit
readonly
unchecked
decimal
in
ref
unsafe
default
int
return
ushort
delegate
interface
sbyte
using
do
internal
sealed
virtual
double
is
short
void
else
lock
sizeof
while

Avoiding conflicts

If you really want to use an identifier that clashes with a keyword, you can qualify 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.

Contextual keywords

Some keywords are contextual, meaning that they can also be used as identifiers—without an @ symbol. The following are contextual keywords:
add
get
let
set
ascending
global
on
value
by
group
orderby
var
descending
in
partial
where
equals
into
remove
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 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 instances 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 fits 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 C# does not predefine (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 following predefined numeric types.
C# type
System type
Suffix
Size
Range
Integral—signed
sbyte
SByte
8 bits
–27 to 27–1
short
Int16
16 bits
–215 to 215–1
int
Int32
32 bits
–231 to 231–1
long
Int64
L
64 bits
–263 to 263–1
Integral—unsigned
byte
Byte
8 bits
0 to 28–1
ushort
UInt16
16 bits
0 to 216–1
uint
UInt32
U
32 bits
0 to 232–1
ulong
UInt64
UL
64 bits
0 to 264–1
Real
float
Single
F
32 bits
±( ~10–45 to 1038)
double
Double
D
64 bits
±( ~10–324 to 10308)
decimal
Decimal
M
128 bits
±( ~10–28 to 1028)
Of the integral types, int and long are first-class citizens and C# and the runtime favor both. 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;

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.Write(       1.0.GetType( ));  // Double (double)
	Console.Write(      1E06.GetType( ));  // Double (double)
	Console.Write(         1.GetType( ));  // Int32 (int)
	Console.Write(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 (zero or one) of storage, the runtime will use one or two bytes of memory, as this is the minimum chunk that the runtime and processor can efficiently work with. To avoid space-inefficiency in the case of arrays, the Framework provides a BitArray class in the System.Collections namespace, which is designed to use just one bit per Boolean value.
= = 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, y = 2, 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 previous section "Real Number Rounding Errors"). The comparison operators also work on enum type members, by comparing their underlying integral values.
We'll explain the equality and comparison operators in greater detail, later, in the "The object Type" and "Operator Overloading" sections.
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 Use Umbrella 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 (as umbrellas are useless in the wind):
	static bool UseUmbrella (bool rainy, bool sunny,
	                         bool windy)
	{
	return ! windy && (rainy || sunny);
	}
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 it 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 = '\\';
The escape sequence characters are outlined below.
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) represents an immutable sequence of Unicode characters. A string literal is specified inside double quotes:
	string a = "Heat";
string is a reference type, not a value type. Its equality operators, however, implement value-type semantics.
The escape sequences that are valid for char literals also work inside strings:
	string a = "Here's a tab:\t";
The cost being 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";
A verbatim string literal can also span multiple lines:
	string escaped  = "First Line\r\nSecond Line";
	string verbatim = @"First Line
	Second Line";

	Console.WriteLine (escaped == verbatim); // True
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 zero. 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
Arrays also implement IEnumerable<T>, so you can enumerate members with the foreach statement:
	foreach (char c in vowels) Console.Write (c);   // 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, which defines common methods and properties for all arrays. This includes instance properties such as Length and Rank, and static methods to:
  • Dynamically create an array (CreateInstance)
  • Get and set elements regardless of the array type (GetValue/SetValue)
Search a sorted array (BinarySearch) or an unsorted array (IndexOf, LastIndexOf, Find, FindIndex, FindLastIndex)
  • Sort an array (Sort)
  • Copy an array (Copy)
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. Because int is a value type, it allocates 1,000 integers in one contiguous block of memory. The default value for each element will be 0:
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.

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.

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, the StringBuilder object is created on the heap, while the sb reference is created on the stack:
	static void Test()
	{
	  StringBuilder sb = new StringBuilder();
	  Console.WriteLine (sb.Length);
	}
After the Test method finishes, sb pops off the stack, and the StringBuilder object is no longer referenced, so it becomes eligible for garbage collection.
Value type instances (and object references) live wherever the variable was declared. If the instance was declared as a field within an object, or as an array element, that instance lives on the heap.
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—because 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 a variable the result of another expression. For example:
	x = x * 5
An assignment expression is not a void expression. It actually carries the assignment value and thus 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

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
A namespace is a domain within which type names must be unique. Types are typically organized into hierarchical namespaces—both to avoid naming conflicts and to make type names easier to find. For example, the RSA type, which handles public key encryption, is defined within the following namespace:
	System.Security.Cryptography
A namespace forms an integral part of a type's name. The following code calls RSA's Create method:
	System.Security.Cryptography.RSA rsa =
	  System.Security.Cryptography.RSA.Create( );
Namespaces are independent of assemblies, which are units of deployment such as an .exe or .dll. Namespaces also have no impact on member visibility—public, internal, private, and so on.
The namespace keyword defines a type within a namespace. For example:
	namespace Outer.Middle.Inner
	{
	  class Class1 {}
	  class Class2 {}
	}
The dots in the namespace indicate a hierarchy of nested namespaces. The following is semantically identical to the preceding example:
	namespace Outer
	{
	  namespace Middle
	  {
	    namespace Inner
	    {
	      class Class1 {}
	      class Class2 {}
	    }
	  }
	}
You can refer to a type with is fully qualified name, which includes all namespaces from the outermost to the innermost. For example, we could refer to Class1 in the preceding example as Outer.Middle.Inner.Class1.
Types not defined in any namespace are said to reside in the global namespace. The global namespace also includes top-level namespaces, such as Outer in our example.
The using directive imports a namespace. This is a convenient way to refer to types without their fully qualified names. For example:
	using Outer.Middle.Inner;

	class Test    // Test is in the global namespace
	{
	  static void Main( )
	  {
	   Class1 c;    // Don't need fully qualified name
	  }
	}
A using directive can be nested within a namespace itself to limit the scope of the directive.

Name scoping

All names present in outer namespaces are implicitly imported into inner namespaces. In this example, the names
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Classes
A class is the most common kind of reference type. The simplest possible class declaration is as follows:
	class YourClassName
	{
	}
A more complex class optionally has:
preceding the keyword class
attributes and class modifiers. The non-nested class modifiers are public, internal, abstract, sealed, static, unsafe, and partial
following YourClassName
generic type parameters, a base class, and interfaces
within the braces
class members (these are methods, properties, indexers, events, fields, constructors, operator functions, nested types, and a finalizer)
A field is a variable that is a member of a class or struct. For example:
	class Octopus
	{
	  string name;
	  public int Age = 10;
	}
A field may have the readonly modifier to prevent it from being modified after construction. A read-only field can be assigned only in its declaration or within the enclosing type's constructor.

Field initialization

Field initialization is optional. An uninitialized field has a default value (0, \0, null, false). Field initializers run before constructors:
	string name = "anonymous";

Declaring multiple fields together

For convenience, you may declare multiple fields of the same type in a comma-separated list. This is a convenient way for all the fields to share the same attributes and field modifiers. For example:
	static readonly int legs = 8, eyes = 1;
A method performs an action in a series of statements. A method can receive input data from the caller by specifying parameters, and output data back to the caller by specifying a return type. A method can specify a void return type, indicating that it doesn't return any value to its caller. A method can also output data back to the caller via ref/out parameters.
A method's signature must be unique within the type. A method's signature comprises its name and parameter types (but not the parameter names, nor the return type).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Inheritance
A class can inherit from another class to extend or customize the original class. Inheriting from a class lets you reuse the functionality in that class instead of building it from scratch. A class can inherit from only a single class, but can itself be inherited by many classes, thus forming a class hierarchy. In this example, we start by defining a class called Asset:
	public class Asset
	{
	  public string Name;
	}
Next, we define classes called Stock and House, which will inherit from Asset. Stock and House get everything an Asset has, plus any additional members that they define:
	public class Stock : Asset   // inherits from Asset
	{
	  public long SharesOwned;
	}

	public class House : Asset   // inherits from Asset
	{
	  public decimal Mortgage;
	}
Here's how we can use these classes:
	Stock msft = new Stock { Name="MSFT",
	                         SharesOwned=1000 };

	Console.WriteLine (msft.Name);         // MSFT
	Console.WriteLine (msft.SharesOwned);  // 1000

	House mansion = new House { Name="Mansion",
	                            Mortgage=250000 };

	Console.WriteLine (mansion.Name);      // Mansion
	Console.WriteLine (mansion.Mortgage);  // 250000
The subclasses Stock and House inherit the Name property from the base class Asset.
A subclass is also called a derived class. A base class is also called a superclass.
References are polymorphic, which means a reference to a base class can refer to an instance of a subclass. For instance, consider the following method:
	public static void Display (Asset asset)
	{
	  System.Console.WriteLine (asset.Name);
	}
This method can display both a Stock and a House because they are both Assets:
	Stock msft    = new Stock ... ;
	House mansion = new House ... ;

	Display (msft);
	Display (mansion);
Polymorphism works on the basis that subclasses (Stock and House) have all the features of their base class (Asset). The converse, however, is not true. If Display was modified to accept a House, you could not pass in an Asset.
An object reference can be:
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 object Type
object (System.Object) is the ultimate base class for all types. Any type can be upcast to object.
To illustrate how this is useful, consider a general-purpose stack. A stack is a data structure based on the principle of LIFO——"Last in, First out." A stack has two operations: push an object on the stack, and pop an object off the stack.
Here is a simple implementation that can hold up to 10 objects:
	public class Stack
	{
	  int position;
	  object[] data = new object[10];

	  public void Push (object obj)
	    { data[position++] = obj; }

	  public object Pop()
	   { return data[--position]; }
	}
Because Stack works with the object type, we can Push and Pop instances of any type to and from the Stack:
	Stack stack = new Stack();
	stack.Push ("sausage");

	// Explicit cast is needed because we're downcasting:
	string s = (string) stack.Pop();

	Console.WriteLine (s);             // sausage
object is a reference type, by virtue of being a class. Despite this, value types, such as int, can also be cast to and from object, and so be added to our stack. This feature of C# is called type unification:
	stack.Push (3);
	int three = (int) stack.Pop();
When you cast between a value type and object, the CLR must perform some special work to bridge the difference in semantics between value and reference types. This process is called boxing and unboxing.
Boxing is the act of casting a value type instance to a reference type instance. The reference type may be either the object class, or an interface (which we will visit later). In this example, we box an int into an object:
	int x = 9;
	object obj = x;          // box the int
Unboxing reverses the operation, by casting the object back to the original value type:
	int y = (int)obj;        // unbox the int
Unboxing requires an explicit cast. The runtime checks that the stated value type (exactly) matches the actual object type, and throws an InvalidCastException if the check fails. For instance, the following throws an exception because
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Structs
A struct is similar to a class, with the following key differences:
  • A struct is a value type, whereas a class is a reference type.
  • A struct does not support inheritance (other than implicitly deriving from object).
A struct can have all the members a class can, except:
  • A parameterless constructor
  • A finalizer
  • Virtual members
A struct is used instead of a class when value type semantics are desirable. Good examples of structs are numeric types, where it is more natural for assignment to copy a value rather than a reference. Because a struct is a value type, each instance does not require instantiation of an object on the heap. This can be important when creating many instances of a type, for example, with an array.
The construction semantics of a struct are as follows:
  • A parameterless constructor implicitly exists, which you can't override. This performs a bitwise-zeroing of its fields.
  • When you define a struct constructor, you must explicitly assign every field.
  • You can't have field initializers in a struct.
Here is an example of declaring and calling struct constructors:
	public struct Point
	{
	  int x, y;
	  public Point (int x, int y) {this.x = x; this.y = y;}
	}

	...
	Point p1 = new Point ();     // p1.x and p1.y will be 0
	Point p2 = new Point (1, 1); // p1.x and p1.y will be 1
The next example generates three compile-time errors:
	public struct Point
	{
	  int x = 1;         // Illegal, cannot initialize field
	  int y;
	  public Point() {}  // Illegal, cannot have
	                     // parameterless constructor

	  public Point(int x) {this.x = x;}   // illegal, must
	                                      // assign field y
	}
Changing struct to class makes this example legal.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Access Modifiers
To promote encapsulation, a type or type member may limit its accessibility to other types and other assemblies by adding one of five access modifiers to the declaration:
public
Fully accessible. The implicit accessibility for members of an enum or interface.
internal
Accessible only within containing assembly or friend assemblies. The default accessibility for nonnested types.
private
Visible only within containing type. The default accessibility members of a class or struct.
protected
Visible only within containing type or subclasses.
protected internal
The union of protected and internal accessibility. (This is less restrictive than protected or internal alone.)
The CLR has the concept of the intersection of protected and internal accessibility, but C# does not support this.
Class2 is accessible from outside its assembly; Class1 is not:
	class Class1        { } // Class1 is internal (default)
	public class Class2 { }
ClassB exposes field x to other types in the same assembly; ClassA does not:
	class ClassA { int x;          }  // x is private
	                                  // (default)
	class ClassB { internal int x; }
Functions within Subclass can call Bar but not Foo:
	class BaseClass
	{
	  void Foo()           { }   // Foo is private (default)
	  protected void Bar() { }
	}

	class Subclass : BaseClass
	{
	   void Test1() { Foo(); }  // Error: cannot access Foo
	   void Test2() { Bar(); }  // OK
	}
A type caps the accessibility of its declared members. The most common example of capping is when you have an internal type with public members. For example:
	class C { public void Foo() {} }
C's (default) internal accessibility caps Foo's accessibility, effectively making Foo internal. The reason Foo would be marked public is to make for easier refactoring, should C later be changed to public.
When overriding a base class function, accessibility must be identical on the overridden function.
The compiler also prevents any inconsistent use of access modifiers. For example, a subclass itself can be less accessible than a base class, but not more accessible:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Interfaces
Content preview·Buy PDF of this chapter|