By Ben Albahari, Peter Drayton, Brad Merrill
Price: $29.99 USD
£17.50 GBP
Cover | Table of Contents | Colophon
get/set methods are logically grouped,
documentation is embedded directly in a declaration, etc.
Furthermore, because declaration order is irrelevant, types
don't require a separate stub declaration to be used
by another type.
get/set methods are logically grouped,
documentation is embedded directly in a declaration, etc.
Furthermore, because declaration order is irrelevant, types
don't require a separate stub declaration to be used
by another type.
namespace FirstProgram {
using System;
class Test {
static void Main ( ) {
Console.WriteLine ("Welcome to C#!");
}
}
}
Test that
contains a method, named Main, that writes
Welcome to
C#! to the Console window. The
Console class encapsulates standard input/output
functionality, providing methods such as
WriteLine. To use types from another namespace, we
use the using directive. Since the
Console class resides in the
System namespace, we write
using System; similarly, types
from other namespaces could use our Test class by
including the following statement: using
FirstProgram;.
/debug
option to the csc command line to include
debugging symbols in the output. This will let you run your program
under a debugger and get meaningful stack traces that include line
numbers.
Main
entry point. Note that Main must be specified as
static.
Main as the
default entry point of execution.
Korn @Korn
Counter and another type called
Test that uses instances of the
Counter. The Counter type uses
the predefined type int, and the
Test type uses the static function member
WriteLine of the Console class
defined in the System namespace:
// Imports types from System namespace, such as Console
using System;
class Counter { // New types are typically classes or structs
// --- Data members ---
int value; // field of type int
int scaleFactor; // field of type int
// Constructor, used to initialize a type instance
public Counter(int scaleFactor) {
this.scaleFactor = scaleFactor;
}
// Method
public void Inc( ) {
value+=scaleFactor;
}
// Property
public int Count {
get {return value; }
}
}
class Test {
// Execution begins here
static void Main( ) {
// Create an instance of counter type
Counter c = new Counter(5);
c.Inc( );
c.Inc( );
Console.WriteLine(c.Count); // prints "10";
// Create another instance of counter type
Counter d = new Counter(7);
d.Inc( );
Console.WriteLine(d.Count); // prints "7";
}
}Korn @Korn
Counter and another type called
Test that uses instances of the
Counter. The Counter type uses
the predefined type int, and the
Test type uses the static function member
WriteLine of the Console class
defined in the System namespace:
// Imports types from System namespace, such as Console
using System;
class Counter { // New types are typically classes or structs
// --- Data members ---
int value; // field of type int
int scaleFactor; // field of type int
// Constructor, used to initialize a type instance
public Counter(int scaleFactor) {
this.scaleFactor = scaleFactor;
}
// Method
public void Inc( ) {
value+=scaleFactor;
}
// Property
public int Count {
get {return value; }
}
}
class Test {
// Execution begins here
static void Main( ) {
// Create an instance of counter type
Counter c = new Counter(5);
c.Inc( );
c.Inc( );
Console.WriteLine(c.Count); // prints "10";
// Create another instance of counter type
Counter d = new Counter(7);
d.Inc( );
Console.WriteLine(d.Count); // prints "7";
}
}
( ):
int x = 123456; // int is a 4-byte integer long y = x; // implicit conversion to 8-byte integer short z =(short)x; // explicit conversion to 2-byte integer
using System;
class Test {
int v;
// Constructors that initialize an instance of a Test
public Test( ) {} // v will be automatically assigned to 0
public Test(int a) { // explicitly assign v a value
v = a;
}
static void Main( ) {
Test[] iarr = new Test [2]; // declare array
Console.WriteLine(iarr[1]); // ok, elements assigned to null
Test t;
Console.WriteLine(t); // error, t not assigned
}
}
|
Type
|
Default value
|
|---|---|
|
Numeric
|
0 |
|
Bool
|
false |
|
Char
|
'\0' |
|
Enum
|
0 |
|
Reference
|
null |
((1 + 2) / 3)
1 + 2 + 3 * 4
((1 + 2) + (3 * 4))
|
Category
|
Operators
|
|---|---|
|
Primary
|
Grouping:
(x)Member access: x.y
Struct pointer member access: ->
Method call: f(x)Indexing: a[x]Post increment: x++Post decrement: x--Constructor call: newArray stack allocation: stackallocType retrieval: typeof
Struct size retrieval: sizeofArithmetic check on: checked Arithmetic check off: unchecked
|
|
Unary
|
Positive value of (passive):
+Negative value of: -Not:! Bitwise complement: ~ Pre increment: ++x Pre decrement:-- x Type cast: (T)x
Value at address: * Address of value: &
|
|
Multiplicative
|
({
}) to form a statement block.
A statement
block can be used anywhere a single statement is valid.
Syntax: |
|---|
[variable =]? expression; |
new, ++, or
--). An expression statement ends in a
semicolon
(;). For example:
x = 5 + 6; // assign result x++; // side effect y = Math.Min(x, 20); // side effect and assign result Math.Min (x, y); // discards result, but ok, there is a side effect x == y; // error, has no side effect, and does not assign result
Variable declaration syntax: |
|---|
type [variable [ = expression ]?]+ ; |
Constant declaration syntax: |
|---|
const type [variable = constant-expression]+ ; |
bool a = true;
while(a) {
int x = 5;
if (x==5) {
int y = 7;
int x = 2; // error, x already defined
}
Console.WriteLine(y); // error, y is out of scope
}
const double speedOfLight = 2.99792458E08; speedOfLight+=10; // error
Namespace declaration syntax: |
|---|
namespace name+a {
using-statement*
[namespace-declaration | type-declaration]*b
}
a Dot-delimited.
b No delimiters.
|
namespace MyCompany.MyProduct.Drawing {
class Point {int x, y, z;}
delegate void PointInvoker(Point p);
}
namespace MyCompany {
namespace MyProduct {
namespace Drawing {
class Point {int x, y, z;}
delegate void PointInvoker(Point p);
}
}
}
Point class from another namespace, you may
refer to it with its fully qualified name:
namespace TestProject {
class Test {
static void Main( ) {
MyCompany.MyProduct.Drawing.Point x;
}
}
}object class,
which all objects implicitly inherit from. Inheriting from a class
requires specifying the class to inherit from in the class
declaration, using the C++ colon notation:
class Location { // Implicitly inherits from object
string name;
// The constructor that initializes Location
public Location(string name) {
this.name = name;
}
public string Name {get {return name;}}
public void Display( ) {
Console.WriteLine(Name);
}
}
class URL : Location { // Inherit from Location
public void Navigate( ) {
Console.WriteLine("Navigating to "+Name);
}
// The constructor for URL, which calls Location's constructor
public URL(string name) : base(name) {}
}
URL has all the members of
Location and a new member,
Navigate:
class Test {
public static void Main( ) {
URL u = new URL("http://microsoft.com");
u.Display( );
u.Navigate( );
}
}
URL u = new URL("http://microsoft.com");
Location l = u; // upcast
u = (URL)l; // downcast
InvalidCastException is thrown.
as operator allows a downcast that
evaluates to null to be made if the downcast
fails:
u = l as URL;
is operator can test if an
objectis or derives from a specified class (or
implements an interface). It is often used to perform a test before a
downcast:
if (l is URL) ((URL)l).Navigate( );
public internal A is
accessible only from within A. This is the default
accessibility for nonnested types, so it may be omitted.
private T is accessible only from
within T. This is the default accessibility for
class and struct members, so it may be omitted.
protected C is accessible only from
within C or from within a class that derives from
C.
protected internal C and assembly
A is accessible only from within
C, from within a class that derives from
C, or from within A. Note that
C# has no concept of protected and
internal, in which a type member in class
C and assembly A is accessible
only from within C or from within a class that
derives from C and is within A.
// Assembly1.dll
using System;
public class A {
private int x=5;
public void Foo( ) {Console.WriteLine (x);}
protected static void Goo( ) {}
protected internal class NestedType {}
}
internal class B {
private void Hoo ( ) {
A a1 = new A ( ); // ok
Console.WriteLine(a1.x); // error, A.x is private
A.NestedType n; // ok, A.NestedType is internal
A.Goo( ); // error, A's Goo is protected
}
}
// Assembly2.exe (references Assembly1.dll)
using System;
class C : A { // C defaults to internal
static void Main( ) { // Main defaults to private
A a1 = new A( ); // ok
a1.Foo( ); // ok
C.Goo( ); // ok, inherits A's protected static member
new A.NestedType( ); // ok, A.NestedType is protected
new B( ); // error, Assembly 1's B is internal
Console.WriteLine(x); // error, A's x is private
}
}Class declaration syntax: |
|---|
attributes? unsafe? access-modifier? new? [ abstract | sealed ]? class class-name [: base-class | : interface+ | : base-class, interface+ ]? { class-members } |
Struct declaration syntax: |
|---|
attributes? unsafe? access-modifier? new? struct struct-name [: interface+]? { struct-members } |
class ExampleClass {
int x; // data member
void Foo( ) {} // function member
struct MyNestedType {} // type member
}
object and is implicitly
sealed. Both classes and structs can implement
interfaces.
Syntax: |
|---|
attributes? unsafe? access-modifier? new? interface interface-name [ : base-interface+ ]? { interface-members } |
abstract class, which is an abstract class
consisting of only abstract members.
public interface IDelete {
void Delete( );
}
TextBox
or TreeView, or your own custom GUI control, can
implement the IDelete interface:
public class TextBox : IDelete {
public void Delete( ) {...}
}
public class TreeView : IDelete {
public void Delete( ) {...}
}
public class TextBox : Control, IDelete {...}
public class TreeView : Control, IDelete {...}
Syntax: |
|---|
type[*]+ array-name = new type [ dimension+ ][*]*; |
[*] is the set: [] [,] [,,]
...
System.Array and are declared in C# using
brackets ([]). For instance:
char[] vowels = new char[] {'a','e','i','o','u'};
Console.WriteLine(vowels [1]); // Prints "e"
System.Collection classes provide dynamically
sized arrays, as well as other data structures, such as associative
(key/value) arrays (see Section 3.4 in Chapter 3).
// rectangular
int [,,] matrixR = new int [3, 4, 5]; // creates one big cube
// jagged
int [][][] matrixJ = new int [3][][];
for (int i = 0; i < 3; i++) {
matrixJ[i] = new int [4][];
for (int j = 0; j < 4; j++)
matrixJ[i][j] = new int [5];
}
// assign an element
matrixR [1,1,1] = matrixJ [1][1][1] = 7;
int[,] array = {{1,2},{3,4}};
GetLength method
returns the number of elements for a given dimension, which is
counted from
(the outermost) to the array's Rank-1 (the
innermost):
// one-dimensional for(int i = 0; i < vowels.Length; i++); // multidimensional for(int i = 0; i < matrixR.GetLength(2); i++);
IndexOutOf-RangeException thrown for
invalid indexes. As in Java, bounds checking
prevents program faults and debugging difficulties while enabling
code to be executed with security restrictions.
Syntax: |
|---|
attributes? access-modifier? new? enum enum-name [ : integer type ]? { [attributes? enum-member-name [ = value ]? ]* } |
public enum Direction {North, East, West, South}
Direction walls = Direction.East;
[Flags]
public enum Direction : byte {
North=1, East=2, West=4, South=8
}
Direction walls = Direction.North | Direction.West;
if((walls & Direction.North) != 0)
System.Console.WriteLine("Can't go north!");
[Flags] attribute is optional. It informs the
runtime that the values in the enum can be bit-combined and should be
decoded accordingly in the debugger or when outputting text to the
console. For example:
Console.WriteLine(walls); // Displays "North, West"
Console.WriteLine(walls.ToString("d")); // displays "5"
System.Enum type also provides many useful static
methods for enums that allow you to determine the underlying type of
an enum, check if a specific value is supported, initialize an enum
from a string constant, retrieve a list of the valid values, and
perform other common operations such as conversions. Here is an
example:
using System;
public enum Toggle : byte { Off=0, On=1 }
class TestEnum {
static void Main( ) {
Type t = Enum.GetUnderlyingType(typeof(Toggle));
Console.WriteLine(t); // Prints "System.Byte"
bool bDimmed = Enum.IsDefined(typeof(Toggle), "Dimmed");
Console.WriteLine(bDimmed); // Prints "False"
Toggle tog =(Toggle)Enum.Parse(typeof(Toggle), "On");
Console.WriteLine(tog.ToString("d")); // Prints "1"
Console.WriteLine(tog); // Prints "On"
Array oa = Enum.GetValues(typeof(Toggle));
foreach(Toggle atog in oa) // Prints "Off=0, On=1"
Console.WriteLine("{0}={1}", atog, atog.ToString("d"));
}
}Syntax: |
|---|
attributes? unsafe? access-modifier? new? delegate [ void | type ] delegate-name (parameter-list); |
delegate bool Filter(string s);
bool and have a single
string parameter. In the following example a
Filter is created that holds the
FirstHalfOfAlphabet method. You then pass the
Filter to the Display method,
which invokes the Filter:
class Test {
static void Main( ) {
Filter f = new Filter(FirstHalfOfAlphabet);
Display(new String [] {"Ant","Lion","Yak"}, f);
}
static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}
static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names)
if(f(s)) // invoke delegate
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
MethodInvoker, which can
hold and then invoke the Foo and
Goo methods sequentially. The
+= method creates a new delegate by adding the
right delegate operand to the left delegate operand.
using System;
delegate void MethodInvoker( );
class Test {
static void Main( ) {
new Test( ); // prints "Foo", "Goo"
}
Test( ) {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m += new MethodInvoker(Goo);
m( );
}
void Foo( ) {
Console.WriteLine("Foo");
}
void Goo( ) {
Console.WriteLine("Goo");
}
}
-= operator:
Test( ) {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m -= new MethodInvoker(Foo);
m( ); // m is now null, throws NullReferenceException
}
delegate void MoveEventHandler(object source, MoveEventArgs e);
System.EventArgs and stores data about
the event.
EventArgs to
include information relevant to a particular event:
public class MoveEventArgs : EventArgs {
public int newPosition;
public bool cancel;
public MoveEventArgs(int newPosition) {
this.newPosition = newPosition;
}
}
Slider class has a Position
property that fires a Move event whenever its
Position changes:
class Slider {
int position;
public event MoveEventHandler Move;
public int Position {
get { return position; }
set {
if (position != value) { // if position changed
if (Move != null) { // if invocation list not empty
MoveEventArgs args = new MoveEventArgs(value);
Move(this, args); // fire event
if (args.cancel)
return;
}
position = value;
}
}
}
}
event keyword promotes encapsulation by
ensuring that only the += and
-= operations can be performed on the
delegate. This means other classes can register themselves to be
notified of the event, but only the Slider can
invoke the delegate (fire the event) or clear the
delegate's invocation list.
Form to act on
changes made to a Slider's
Position. You do this by creating a
Syntax: |
|---|
try statement-block [catch (exception type value?)? statement-block]+ | finally statement-block | [catch (exception type value?)? statement-block]+ finally statement-block |
try statement is to simplify dealing with
program execution in exceptional circumstances. A
try statement does two things. First, it lets
exceptions thrown during the try
block's execution be caught by the
catch block. Second, it ensures that
execution can't leave the try
block without first executing the
finally block. A try
block must be followed by one or more catch
blocks, a finally block, or both.
Account
class. An exceptional state for the account class is when its balance
is below zero, which occurs when the Withdraw
method receives a withdraw amount larger than the current balance.
Our test class makes our customer tiffanyTaylor
perform several actions that involve despositing and withdrawing from
an account. When she attempts to withdraw more money than she has in
her account, a FundException is thrown, which we
will catch so we can notify the user and display her current account
balance.
using System;
public class FundException : Exception {
public FundException(decimal balance) :
base("Total funds are "+balance+" dollars") {}
}
class Account {
decimal balance;
public decimal Balance {
get {return balance;}
}
public void Deposit(decimal amount) {
balance += amount;
}
public void Withdraw(decimal amount) {
if (amount > balance)
throw new FundException(balance);
balance -= amount;
}
}
class Customer {
Account savingsAccount = new Account( );
public void SellBike( ) {
savingsAccount.Deposit (1000);
}
public void BuyCar( ) {
savingsAccount.Withdraw (30000);
}
public void GoToMovie( ) {
savingsAccount.Withdraw (20);
}
}
class Test {
static void Main( ) {
Customer tiffanyTaylor = new Customer( );
bool todaysTasksDone = false;
try {
tiffanyTaylor.SellBike( );
tiffanyTaylor.BuyCar( );
tiffanyTaylor.GoToMovie( );
todaysTasksDone = true;
}
catch (FundException ex) {
Console.WriteLine(ex.Message);
}
finally {
Console.WriteLine(todaysTasksDone);
}
}
}Syntax: |
|---|
[[target:]? attribute-name ( positional-param+ | [named-param = expression]+ | positional-param+, [named-param = expression]+)?] |
ref modifier in C#. The limitation of
this approach is that you can only associate information with code
elements using the predefined constructs that the language itself
provides.
System.Attribute. When specifying an attribute on an
element, the attribute name is the name of the type. By convention
the derived type name ends with the word
"Attribute", but this suffix
isn't required.
Foo class is
serializable using the Serializable attribute:
[Serializable]
public class Foo {...}
Serializable attribute is actually a type
declared in the System namespace, as follows:
class SerializableAttribute : Attribute {...}
Serializable attribute
using its fully qualified typename, as follows:
[System.SerializableAttribute]
public class Foo {...}
Serializable attribute are semantically identical.
/unsafe compiler option. Pointer types are
primarily useful for interoperability with C APIs but may also be
used for accessing memory outside the managed heap or for
performance-critical hotspots.
|
Operator
|
Meaning
|
|---|---|
& |
The address-of operator returns a pointer to the
address of a value.
|
* |
The dereference operator returns the value at
the address of a pointer.
|
-> |
The pointer-to-member operator is a syntactic
shortcut, in which x->y is equivalent to
|