Cover | Table of Contents
http://msdn.microsoft.com/net/sscli/), as
well as to Open Source implementations of .NET called
DotGNU
Portable .NET (http://www.dotgnu.org) and
Mono (http://www.go-mono.com). All three
implementations include support for C#.
@ prefix may
be used to avoid a clash with a keyword, but is not considered part
of the identifier. For instance, the following two identifiers are
equivalent:
@ prefix may
be used to avoid a clash with a keyword, but is not considered part
of the identifier. For instance, the following two identifiers are
equivalent:
abstract
|
as
|
base
|
bool
|
break
|
byte
|
case
|
catch
|
char
|
checked
|
class
|
const
|
continue
|
decimal
|
default
|
delegate
|
do
|
double
|
else
|
enum
|
event
|
explicit
|
int
type (which holds an integer) or the bool type
(which holds a true or false
value). The key characteristic of a value type is a copy made of the
value that is assigned to another value. For example:
using System;
class Test {
static void Main ( ) {
int x = 3;
int y = x; // assign x to y, y is now a copy of x
x++; // increment x to 4
Console.WriteLine (y); // prints 3
}
}
y is updated here, while
y remained unchanged earlier:
using System;
using System.Text;
class Test {
static void Main ( ) {
StringBuilder x = new StringBuilder ("hello");
StringBuilder y = x;
x.Append (" there");
Console.WriteLine (y); // prints "hello there"
}
}
StringBuilder type is a
reference type, while the int type is a value
type. When we declared the StringBuilder variable,
we were actually doing two different things, which can be separated
into these two lines:
StringBuilder x;
x = new StringBuilder ("hello");
StringBuilder object. The second line assigns a
new StringBuilder object to the variable.
Let's look at the next line:
StringBuilder y = x;
System namespace. For
example, there is only a syntactic difference between these two
statements:
int i = 5; System.Int32 i = 5;
|
C# type
|
System type
|
Size
|
Signed
|
|---|---|---|---|
sbyte
|
System.SByte
|
1 byte
|
Yes
|
short
|
System.Int16
|
2 bytes
|
Yes
|
int
|
System.Int32
|
4 bytes
|
Yes
|
long
|
System.Int64 |
int[] nums = new int[2];
nums[0] = 100;
nums[1] = 200;
char[ ] vowels = new char[ ] {'a','e','i','o','u'};
Console.WriteLine(vowels [1]); // Prints "e"
"e" because array indexes
start at 0. To support other languages, .NET can create arrays based
on arbitrary start indexes, but all the libraries use zero-based
indexing. Once an array is created, its length cannot be changed.
However, the
System.Collection
classes provide dynamically sized
arrays, as well as other data structures, such as associative
(key/value) arrays.
for loop, which is explained later in Section 1.8. The
for loops here simply iterate through each item in
the arrays.
// rectangular
int [,,] matrixR = new int [3, 4, 5]; // creates 1 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
from 0 (the outermost) to the array's
using System;
class Test {
int v;
// Constructors that initalize 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[ ] tests = new Test [2]; // declare array
Console.WriteLine(tests[1]); // ok, elements assigned
// to null
Test t;
Console.WriteLine(t); // error, t not assigned before
// use
}
}
|
Type
|
Default value
|
|---|---|
|
Numeric types
|
0
|
((1 + 2) / 3)
1 + 2 + 3 * 4
((1 + 2) + (3 * 4))
|
Category
|
Operators
|
Examples
|
|---|---|---|
|
Primary
|
Grouping:
Member access:
|
new,
++, --). An expression
statement ends in a semicolon. For example:
int x = 5 + 6; // assign result
x++; // side effect
int 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
int x = 100; // variable declaration const int y = 110; // constant declaration
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 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 from another namespace, you may refer to it
with its fully qualified name. The namespace that a type is within
actually becomes part of the type name:
namespace TestProject {
class Test {
static void Main( ) {
MyCompany.MyProduct.Drawing.Point x;
}
}
}
using
keyword is a convenient way to avoid
using the fully qualified name of types in other namespaces. This
example is semantically identical to our previous example:
Planet,
Astronaut, and Test—to
test our simulation.
Planet
class. By convention, we define the data members of the class at the top of
the class declaration. There are two data members here, the
name and gravity fields, which
store the name and gravity of a planet. We then define a constructor
for the planet.
Constructors
are function members that allow
you to initialize an instance of your class. We initialize the data
members with values fed to the parameters of the constructor.
Finally, we define two more function members, which are properties
that allow us to get the "Name" and
"Gravity" of a planet. The
Planet class looks like this:
using System;
class Planet {
string name; // field
double gravity; // field
// constructor
public Planet (string n, double g) {
name = n;
gravity = g;
}
// property
public string Name {
get {return name;}
}
// property
public double Gravity {
get {return gravity;}
}
}
Astronaut class. As with the
Planet class, we first define our data members.
Here an astronaut has two fields: the astronaut's
fitness and the current planet the astronaut is on. We then provide a
constructor, which initializes the fitness of an astronaut. Next, we
define a CurrentPlanet property that allows us to
get or set the planet an astronaut is on. Finally, we define a jump
method that outputs how far the astronaut jumps, based on the fitness
of the astronaut and the planet he is on.
using System;
class Astronaut {
double fitness; // field
Planet currentPlanet; // field
// constructor
public Astronaut (double f) {
fitness = f;
}
// property
public Planet CurrentPlanet {
get {
return currentPlanet;
}
set {
currentPlanet = value;
}
}
// method
public void Jump ( ) {
if (currentPlanet == null)
Console.WriteLine ("Bye Bye!");
else {
double distance = fitness/currentPlanet.Gravity;
Console.WriteLine ("Jumped {0} metres on {1}",
distance,
currentPlanet.Name);
}
}
}// 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
}
}System.ValueType). Both
classes and structs can implement interfaces.
struct Point {
public int x, y;
}
new
operator, which
will initialize all the struct members to their defaults (zero in the
case of x and y). If you do not
use the new operator, you will need to initialize
the struct members yourself. You can also use array declaration
syntax to create an array of structs:
Point p1 = new Point( ); Point p2; p2.x = p2.y = 0; Point[] points = new Point[3];
public interface IDelete {
void Delete( );
}
IDelete interface can be implemented by GUI
controls that support the concept of deleting, such as a
TextBox, TreeView, or your own
custom GUI control.
public class TextBox : IDelete {
public void Delete( ) {...}
}
public class TreeView : IDelete {
public void Delete( ) {...}
}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 and 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((int) walls); // Displays "5"
System.Enum type also provides many useful
static methods for enums that let you 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
other common operations such as conversions. Here is an example of
the usage:
using System;
public enum Toggle : byte { Off=0, On=1 }
class Test {
static void Main( ) {
Type t = Enum.GetUnderlyingType(typeof(Toggle));
Console.WriteLine(t); // Prints "Byte"
bool bDimmed = Enum.IsDefined(typeof(Toggle),
"Dimmed");
Console.WriteLine(bDimmed); // Prints "False"
Toggle tog =(Toggle)Enum.Parse(typeof(Toggle), "On");
Console.WriteLine(Enum.Format(typeof(Toggle), tog,
"D")); // Prints "1"
Console.WriteLine(tog); // Prints "On"
Array oa = Enum.GetValues(typeof(Toggle));
foreach(Toggle toggle in oa) // Prints "On=1, Off=0"
Console.WriteLine("{0}={1}", toggle,
Enum.Format(typeof(Toggle),
toggle, "D"));
}
}using System;
delegate bool Filter (string s);
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);
}
}
params
modifier in its parameter list, which
expands the list of elements that characterize an ordinary method
signature. The actual name of the target method is irrelevant to the
delegate.
MethodInvoker, which we use to 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 is now null
}
public delegate void MoveEventHandler(object source, MoveEventArgs e);
System.EventArgs and
contains data about the event.
EventArgs
class may be derived from to include
information relevant to a particular event:
using System;
public class MoveEventArgs : EventArgs {
public int newPosition;
public bool cancel;
public MoveEventArgs(int newPosition) {
this.newPosition = newPosition;
}
}
Position property that fires a
Move event whenever its
Position changes:
public 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.
Other classes may act on the event, but only the
Slider can invoke the delegate (fire the event) or
clear the delegate's invocation list.
operator
preceding the operator to overload (instead of a method name),
parameters representing the operands, and return types representing
the result of an expression. Table 1-3 lists
the available overloadable operators.
+
|
-
|
!
|
~
|
++
|
--
|
* (binary only) |
/
|
%
|
& (binary only) |
|
|
^
|
<<
|
>>
|
==
|
try
statement is to simplify program
execution in exceptional circumstances—typically, an error. A
try statement does two things. First, it lets the
catch block catch exceptions thrown during the
try block's execution. Second, it
ensures that execution cannot leave the try block
without first executing the finally block. A
try block must be followed by a
catch block(s), a finally
block, or both. The form of a try block looks like
this:
try {
... // exception may be thrown during execution of this
// function
}
catch (ExceptionA ex) {
... // react to exception of type ExceptionA
}
catch (ExceptionB ex) {
... // react to exception of type ExceptionB
}
finally {
... // code to always run after try block executes, even if
... // an exception is not thrown
}
using System;
public class WeightCalculator {
public static float CalcBMI (float weightKilos,
float metersTall) {
if (metersTall < 0 || metersTall > 3)
throw new ArgumentException ("Impossible Height",
"metersTall");
if (metersTall < 0 || weightKilos > 1000)
throw new ArgumentException ("Impossible Weight",
"weightKilos");
return weightKilos / (metersTall*metersTall);
}
}
class Test {
static void Main ( ) {
TestIt ( );
}
static void TestIt ( ) {
try {
float bmi = WeightCalculator.CalcBMI (100, 5);
Console.WriteLine(bmi);
}
catch(ArgumentException ex) {
Console.WriteLine(ex);
}
finally {
Console.WriteLine (
"Thanks for running the program");
}
Console.Read( );
}
}
ref
modifier in C#. The limitation of this approach is that you can
associate information with code elements using only the predefined
constructs that the language provides.
System.Attribute. When specifying an attribute to
an element, the attribute name is the name of the type. By
convention, the derived type name ends in
Attribute, although specifying the suffix is not
required when specifying the attribute.
Foo class is specified as
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 type name, as follows:
[System.SerializableAttribute]
public class Foo {...}
Serializable attribute are semantically identical.
/unsafe
compiler option. Pointer types are primarily useful
for interop 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 (*x).y
|
unsafe
keyword, you're
permitted to use pointer types and perform C++-style pointer
operations on memory within that scope. Here is an example of using
pointers with a
managed object:
#define DEBUG
using System;
class MyClass {
static int x = 5;
static void Main( ) {
# if DEBUG
Console.WriteLine("Testing: x = {0}", x);
# endif
}
}
Foo is compiled as
conditionally dependent upon the presence of the
DEBUG symbol. If we remove the
DEBUG symbol, the statement is not compiled.
Preprocessor symbols can be defined within a source file (as we have
done) and can be passed to the compiler with the
/define: symbol command-line option.
#error and #warning symbols
prevent accidental misuse of conditional directives by making the
compiler generate a warning or error when given an undesirable set of
compilation symbols. See Table 1-5 for a list
of preprocessor directives and their actions.
|
Preprocessor directive
|
Action
|
|---|---|
#define
symbol
|
Defines symbol
|
#undef
symbol
|
Undefines symbol
|
#if
symbol
[
operator
|
System
namespace. This
namespace is the heart of the FCL and contains classes, interfaces,
and attributes on which all other types depend. The root of the FCL
is the type Object
, from which all other
.NET types derive. Other fundamental types are
ValueType
(the base type for
structs), Enum
(the base type for
enums), Convert
(used to convert
between base types),
Exception
(the base type for
all exceptions), and the boxed versions of the predefined value
types. Interfaces that are
used throughout the FCL (such as ICloneable,
IComparable, IFormattable, and
IConvertible) are also defined here. Extended
types such as /reference:<file
list> command-line option for the C#
compiler. However, commonly used assemblies are referenced by
default.
/noconfig
switch with csc.exe to disable the local and
global csc.rsp files entirely.
|
Namespace
|
DLLs
|
|---|---|
Accessibility
|
Accessibility.dll
|
EnvDTE
|
envdte.dll
|
IEHost.Execute
|
IEExecRemote.dll
|
Microsoft.CLRAdmin
|
mscorcfg.dll
|
System.Text.RegularExpressions. (For more
information, see the ".NET Framework Regular
Expressions" topic in the .NET Framework SDK
Documentation.) Each of the modifiers and qualifiers in the tables
can substantially change the behavior of the matching and searching
patterns. For further information on regular expressions, we
recommend the definitive Mastering Regular
Expressions
by Jeffrey E. F.
Friedl
(O'Reilly, 2002).
|
Escape code sequence
|
Meaning
|
Hexadecimal equivalent
|
|---|---|---|
\a
|
Bell
|
|