O'Reilly and Associates

C# Essentials
Examples

[ Back to: C# Essentials ]

Introduction

A Minimal C# Program

class Test {
   static void Main() {
      System.Console.WriteLine("Welcome to C#!");
   }
}

C# Language Reference

Identifiers

Ko|n
@Ko|n

Types

Example: Building and Using Types

// 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";
   }
}

Implicit and Explicit Conversions

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

Predefined Types

int i = 5;
System.Int32 i = 5;

Integral Types

int x = 5;
ulong y = 0x1234AF; // prefix with 0x for hexadecimal

int x = 123456;
long y = x; // implicit, no information lost
short z = (short)x; // explicit, truncates x

Floating Point Types

float x = 9.81f;
double y = 7E-02; // 0.07

int strength = 2;
int offset = 3;
float x = 9.53f * strength - offset;

float x = 3.53f;
int offset = (int)x;

Decimal Type

decimal x = 80603.454327m; // holds exact value

Char Type

'A' // simple character
'\u0041' // Unicode
'\x0041' // unsigned short hexadecimal
'\n' // escape sequence character

String Type

string a = "Heat";

string a1 = "\\\\server\\fileshare\\helloworld.cs";
string a2 = @"\\server\fileshare\helloworld.cs";
Console.WriteLine(a1==a2); // Prints "True"

string b1 = "First Line\r\nSecond Line";
string b2 = @"First Line
Second Line";
Console.WriteLine(b1==b2); // Prints "True"

Types and Memory

Simple Types Are Value Types

int i = 3;
string s = i.ToString();

// This is an explanatory version of System.Int32
namespace System {
  struct Int32 {
    ...
    public string ToString() {
      return ...;
    }
  }
}
// This is valid code, but we recommend you use the int alias
System.Int32 i = 5;

Value Types and Reference Types Side By Side

// Reference-type declaration
class PointR {
  public int x, y;
}
// Value-type declaration
struct PointV {
  public int x, y;
}
class Test {
  static void Main() {
    PointR a; // Local reference-type variable, uses 4 bytes of
              // memory on the stack to hold address
    PointV b; // Local value-type variable, uses 8 bytes of
              // memory on the stack for x and y
    a = new PointR(); // Assigns the reference to address of new
                      // instance of PointR allocated on the
                      // heap. The object on the heap uses 8
                      // bytes of memory for x and y, and an
                      // additional 8 bytes for core object
                      // requirements, such as storing the 
                      // object's type  synchronization state
    b = new PointV(); // Calls the value-type's default
                      // constructor.  The default constructor 
                      // for both PointR and PointV will set 
                      // each field to its default value, which 
                      // will be 0 for both x and y.
    a.x = 7;
    b.x = 7;
  }
}
// At the end of the method the local variables a and b go out of
// scope, but the new instance of a PointR remains in memory until
// the garbage collector determines it is no longer referenced

    ...
    PointR c = a;
    PointV d = b;
    c.x = 9;
    d.x = 9;
    Console.WriteLine(a.x); // Prints 9
    Console.WriteLine(b.x); // Prints 7
  }
}

Type System Unification

Value Types "Expand The Set Of Simple Types"

int[] iarr = new int [1000];

struct PointV {
  public int x, y
}
PointV[] pvarr = new PointV[1000];

class PointR {
   public int x, y;
}
PointR[] prarr = new PointR[1000];
for( int i=0; i<prarr.Length; i++ )
   prarr[i] = new PointR();

Boxing and Unboxing Value Types

class Queue {
  ...
  void Enqueue(object o) {...}
  object Dequeue() {return ...}
}

Queue q = new Queue();
q.Enqueue(9); // box the int
int i = (int)q.Dequeue(); // unbox the int

Variables

Definite Assignment

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[] iarr = new Test [2]; // declare array
    Console.WriteLine(iarr[1]); // ok, elements assigned to null
    Test t;
    Console.WriteLine(t); // error, t not assigned
  }
}

Default Values

int a = 1000000;
int b = 1000000;

// Check an expression
int c = checked(a*b);

// Check every expression in a statement-block
checked {
   ...
   c = a * b;
   ...
}

const int signedBit = unchecked((int)0x80000000);

Statements

Expression Statements

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

Declaration Statements

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

Empty Statements

while(!thread.IsAlive);

Selection Statements

If-Else Statement

int Compare(int a, int b) {
   if (a>b)
      return 1;
   else if (a<b)
      return -1;
   return 0;
}

Switch Statement

void Award(int x) {
  switch(x) {
    case 1:
      Console.WriteLine("Winner!");
      break;
    case 2:
      Console.WriteLine("Runner-up");
      break;
    case 3:
    case 4:
      Console.WriteLine("Highly commended");
      break;
    default:
      Console.WriteLine("Don't quit your day job!");
      break;
  }
}

void Greet(string title) {
  switch (title) {
    case null:
      Console.WriteLine("And you are?");
      goto default;
    case "King":
      Console.WriteLine("Greetings your highness");
      // error, should specify break, otherwise...
    default :
      Console.WriteLine("How's it hanging?");
      break;
  }
}

Loop Statements

While Loops

int i = 0;
while (i<5) {
  i++;
}

Do-While Loops

int i = 0;
do
  i++;
while (i<5);

For Loops

for (int i=0; i<10; i++)
  Console.WriteLine(i);

for (;;)
  Console.WriteLine("Hell ain't so bad");

Foreach Loops

foreach ( type-value in IEnumerable )
  statement or statement-block

for (int i=0; i<dynamite.Length; i++)
  Console.WriteLine(dynamite [i]);

foreach (Stick stick in dynamite)
  Console.WriteLine(stick);

IEnumerator ie = dynamite.GetEnumerator();
while (ie.MoveNext()) {
  Stick stick = (Stick)ie.Current;
  Console.WriteLine(stick);
}

Jump Statements

Break Statement

int x = 0;
while (true) {
  x++;
  if (x>5)
    break; // break from the loop
}

Continue Statement

int x = 0;
int y = 0;
while (y<100) {
  x++;
  if ((x%7)==0)
    continue; // continue with next iteration
  y++;
}

Goto Statement

int x = 4;
start:
x++;
if (x==5)
 goto start;

Return Statement

int CalcX(int a) {
  int x = a * 100;
  return x; // return to the calling method with value
}

Throw Statement

if (w==null)
  throw new Exception("w can't be null");

Organizing Types

Namespaces

namespace MyCompany.MyProduct.Drawing {
  class Point {int x, y, z}
  delegate void PointInvoker(Point p);
}

Nesting Namespaces

namespace MyCompany {
  namespace MyProduct {
    namespace Drawing {
      class Point {int x, y, z}
      delegate void PointInvoker(Point p);
    }
  }
}

Using A Type With Its Fully Qualified Name

namespace TestProject {
  class Test {
    static void Main() {
      MyCompany.MyProduct.Drawing.Point x;
    }
  }
}

Using Keyword

namespace TestProject {
  using MyCompany.MyProduct.Drawing;
  class Test {
    static void Main() {
      Point x;
    }
  }
}

Aliasing Types and Namespaces

using sys = System;        // Namespace alias
using txt = System.String; // Type alias
class Test {
  static void Main() {
    txt s = "Hello, World!";
    sys.Console.WriteLine(s); // Hello, World!
    sys.Console.WriteLine(s.GetType()); // System.String
  }
}

Inheritance

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) {}
}
:
class Test {
  static void Main() {
    URL u = new URL("http://microsoft.com");
    u.Display();
    u.Navigate();
  }
}

Class Conversions

URL u = new URL();
Location l = u; // upcast
u = (URL)l; // downcast

As Operator

u = l as URL;

Is Operator

if (l is URL)
  ((URL)l).Navigate();

Polymorphism

class LocalFile : Location {
  public void Execute() {
    Console.WriteLine("Executing "+Name);
  }
  // The constructor for LocalFile, which calls URL's constructor
  public LocalFile(string name) : base(name) {}
}
class Test {
  static void Main() {
    URL u = new URL();
    LocalFile l = new LocalFile();
    Show(u);
    Show(l);
  }
  public static void Show(Location loc) {
    Console.Write("Location is: ");
    loc.Display();
  }
}

Virtual Function Members

class Location {
  public virtual void Display() {
    Console.WriteLine(Name);
    }
    ...
}
class URL : Location {
  // chop off the http:// at the start
  public override void Display() {
    Console.WriteLine(Name.Substring(6));
  }
  ...
}

Abstract Classes and Abstract Members

abstract class Location {
  public abstract void Launch();
}
class URL : Location {
  public override void Launch() {
    Console.WriteLine("Run Internet Explorer...");
  }
}
class LocalFile : Location {
  public override void Launch() {
    Console.WriteLine("Run Win32 Program...");
  }
}

Sealed Classes

sealed class Math {
  ...
}

Hiding Inherited Members

class B {
  public virtual void Foo() {}
}
class D : B {
  public override void Foo() {}
}
class N : D {
  public new void Foo() {} // hides D's Foo
}
N n = new N();
n.Foo(); // calls N's Foo
((D)n).Foo(); // calls D's Foo
((B)n).Foo(); // calls D's Foo

Versioning Virtual Function Members

class B { // written by the library people
  virtual void Foo() {...} // added in latest update
}
class D : B { // written by you
  void Foo() {...}
}

Access Modifiers

// 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
  }
}

Restrictions on Access Modifiers

Classes and Structs

Instance Members and Static Members

class Panda {
  string name;
  static string speciesName = "Ailuropoda melanoleuca";
  // Initializes Panda(See Instance Constructors)
  public Panda(string name) {
    this.name = name;
  }
  public void PrintName() {
    Console.WriteLine(name);
  }
  public static void PrintSpeciesName() {
    Console.WriteLine(speciesName);
  }
}
class Test {
  static void Main() {
    Panda.PrintSpeciesName(); // invoke static method
    Panda p = new Panda("Petey");
    p.PrintName(); // invoke instance method
  }
}

Fields

class MyClass {
  int x;
  float y = 1, z = 2;
  static readonly int MaxSize = 10;
  ...
}

Constants

public static double Circumference(double radius) {
  return 2 * Math.PI * radius;
}

public static double Circumference(double radius) {
  return 6.2831853071795862 * radius;
}

Properties

public class Well {
  decimal dollars; // private field
  public int Cents {
    get { return(int)(dollars * 100); }
    set {
      // value is an implicit variable in a set
      if (value>=0) // typical validation code
         dollars = (decimal)value/100;
    }
  }
}
class Test {
   static void Main() {
      Well w = new Well();
      w.Cents = 25; // set
      int x = w.Cents; // get
      w.Cents += 10; // get and set(throw a dime in the well)
   }
}

Indexers

public class ScoreList {
  int[] scores = new int [5];
  // indexer
  public int this[int index] {
    get {
      return scores[index]; }
    set {
      if(value >= 0 && value <= 10)
        scores[index] = value;
    }
  }
  // property (read-only)
  public int Average {
    get {
      int sum = 0;
      foreach(int score in scores)
        sum += score;
      return sum / scores.Length;
    }
  }
}
class IndexerTest {
  static void Main() {
    ScoreList sl = new ScoreList();
    sl[0] = 9;
    sl[1] = 8;
    sl[2] = 7;
    sl[3] = sl[4] = sl[1];
    System.Console.WriteLine(sl.Average);
  }
}

Methods

Passing Arguments By Value

static void Foo(int p) {++p;}
static void Main() {
  int x = 8;
  Foo(x); // make a copy of the value-type x
  Console.WriteLine(x); // x will still be 8
}

Ref Modifier

static void Foo(ref int p) {++p;}
static void Test() {
  int x = 8;
  Foo(ref x); // send reference of x to Foo
  Console.WriteLine(x); // x is now 9
}

Out Modifier

using System;
class Test {
  static void Split(string name, out string firstNames, 
                    out string lastName) {
     int i = name.LastIndexOf(' ');
     firstNames = name.Substring(0, i);
     lastName = name.Substring(i+1);
  }
  static void Main() {
    string a, b;
    Split("Nuno Bettencourt", out a, out b);
    Console.WriteLine("FirstName:{0}, LastName:{1}", a, b);
  }
}

Params Modifier

using System;
class Test {
  static int Add(params int[] iarr) {
    int sum = 0;
    foreach(int i in iarr)
      sum += i;
    return sum;
  }
  static void Main() {
    int i = Add(1, 2, 3, 4);
    Console.WriteLine(i); // 10
  }
}

Overloading Methods

void Foo(int x);
viod Foo(double x);
void Foo(int x, float y);
void Foo(float x, int y);
void Foo(ref int x);
void Foo(out int x);

void Foo(int x);
float Foo(int x); // compile error
void Goo (int[] x);
void Goo (params int[] x); // compile error

Operators

Implementing Value Equality

class Note {
  int value;
  public Note(int semitonesFromA) {
    value = semitonesFromA;
  }
  public static bool operator ==(Note x, Note y) {
    return x.value == y.value;
  }
  public static bool operator !=(Note x, Note y) {
    return x.value != y.value;
  }
  public override bool Equals(object o) {
    if(!(o is Note))
      return false;
    return this ==(Note)o;
  }
}
Note a = new Note(4);
Note b = new Note(4);
Object c = a;
Object d = b;

// To compare a and b by reference
Console.WriteLine((object)a ==(object)b; // false 

//To compare a and b by value:
Console.WriteLine(a == b); // true

//To compare c and d by reference:
Console.WriteLine(c == d); // false

//To compare c and d by value:
Console.WriteLine(c.Equals(d)); // true

Custom Implicit and Explicit Conversions

...
// Convert to hertz
public static implicit operator double(Note x) {
  return 440*Math.Pow(2,(double)x.value/12);
}

// Convert from hertz(only accurate to nearest semitone)
public static explicit operator Note(double x) {
  return new Note((int)(0.5+12*(Math.Log(x/440)/Math.Log(2))));
}
...

Note n =(Note)554.37; // explicit conversion
double x = n; // implicit conversion

Three-State Logic Operators

public struct SQLBoolean ... {
  ...
  public static bool operator true(SQLBoolean x) {
    return x.value == 1;
  }
  public static bool operator false(SQLBoolean x) {
    return x.value == -1;
  }
  public static SQLBoolean operator !(SQLBoolean x) {
    return new SQLBoolean(- x.value);
  }
  public bool IsNull {
    get { return value == 0;}
  }
  ...
}
class Test {
  void Foo(SQLBoolean a) {
    if (a)
      Console.WriteLine("True");
    else if (! a)
      Console.WriteLine("False");
    else
      Console.WriteLine("Null");
   }
}

Instance Constructors

class MyClass {
  public MyClass() {
    // initialization code
  }
}

class MyClass {
  public int x;
  public MyClass() : this(5) {}
  public MyClass(int v) {
    x = v;
  }
}
MyClass m1 = new MyClass();
MyClass m2 = new MyClass(10);
Console.WriteLine(m1.x) // 5
Console.Writeline(m2.x) // 10;

Calling Base Class Constructors

class B {
  public int x ;
  public B(int a) {
    x = a;
  }
  public B(int a, int b) {
    x = a * b;
  }
  // Notice how all of B's constructors need parameters
}
class D : B {
  public D() : this(7) {} // call an overloaded constructor
  public D(int a) : base(a) {} // call a base class constructor
}

Field Initialization Order

class MyClass {
  int x = 5;
}

Static Constructors

class Test {
   static Test() {
       Console.WriteLine("Test Initialized");
   }
}

Static Field Initialization Order

class Test {
  public static int x = 5;
  public static void Foo() {}
  static Test() {
    Console.WriteLine("Test Initialized");
  }
}

Non-Determinism Of Static Constructor Calls

class Test2 {
  public static void Foo() {}
  static Test() {
    Console.WriteLine("Test2 Initialized");
  }
}
Test.Foo();
Test2.Foo();

Self Referencing

this Keyword

class Dude {
  string name;
  public Test(string name) {
    this.name = name;
  }
  public void Introduce(Dude a) {
    if (a!=this)
      Console.WriteLine("Hello, I'm "+name);
  }
}

Base Keyword

class Hermit : Dude {
  public void new Introduce(Dude a) {
    base.TalkTo(a);
    Console.WriteLine("Nice Talking To You");
  }
}

Destructors and Finalizers

protected override void Finalize() {
  ...
  base.Finalize();
}

Nested Types

using System;
class A {
  int x = 3; // private member
  protected internal class Nested {// choose any access-level
    public void Foo () {
      A a = new A ();
      Console.WriteLine (a.x); //can access A's private members
    }
  }
}
class B {
  static void Main () {
    A.Nested n = new A.Nested (); // Nested is scoped to A
    n.Foo ();
  }
}
// an example of using "new" on a type declaration
class C : A {
   new public class Nested {} // hide inherited type member
}

Interfaces

Defining an Interface

public interface IDelete {
   void Delete();
}

Implementing an 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 {...}

Using an Interface

class MyForm {
   ...
   void DeleteClick() {
      if (ActiveControl is IDelete) {
         IDelete d = (IDelete)ActiveControl;
         d.Delete();
      }
   }
}

Extending an Interface

ISuperDelete : IDelete {
   bool CanDelete {get;}
   event EventHandler CanDeleteChanged;
}

Explicit Interface Implementation

public interface IDesignTimeControl {
   ...
   object Delete();
}
public class TextBox : IDelete, IDesignTimeControl {
   ...
   void IDelete.Delete() {}
   object IDesignTimeControl.Delete() {...}
   // Note that explicitly implementing just one of them would
   // be enough to resolve the conflict
}

TextBox tb = new TextBox();
IDesignTimeControl idtc = (IDesignTimeControl)tb;
IDelete id = (IDelete)tb;
idtc.Delete();
id.Delete();

Reimplementing An Interface

public class RichTextBox : TextBox, IDelete {
   // TextBox's IDelete.Delete is not virtual (since explicit
   // interface implementations cannot be virtual)
   public void Delete() {}
}

Interface Conversions

interface IDelete {...}
interface IDesigntimeControl {...}
class TextBox : IDelete, IDesignTimeControl {...}
sealed class Timer : IDesignTimeControl {...}

TextBox tb1 = new TextBox ();
IDelete d = tb1; // implicit cast
IDesignTimeControl dtc = (IDesignTimeControl)d;
TextBox tb2 = (TextBox)dtc;
Timer t = (Timer)d; // illegal, a Timer can never implement IDelete

Arrays

char[] vowels = new char[] {'a','e','i','o','u'};
Console.WriteLine(vowels [1]); // Prints "e"

Multi-Dimensional Arrays

// rectangular
int [,,] matrixR = new int [3, 4, 5]; // creates 1 big cube
// jagged
int [][][] matrixJ = new int [3][][];
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;

Local and Field Array Declarations

// single dimensional
for(int i = 0; i < vowels.Length; i++);
// multi-dimensional
for(int i = 0; i < matrixR.GetLength(2); i++);

Bounds Checking

Enums

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!");

Console.WriteLine(walls.Format()); // Displays "North|West"
Console.WriteLine(walls); // Calls walls.ToString, displays "5"

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 "Byte"

    bool bDimmed = Enum.IsDefined(typeof(Toggle), "Dimmed");
    Console.WriteLine(bDimmed); // Prints "False"

    Toggle tog =(Switch)Enum.FromString(typeof(Toggle), "On");
    Console.WriteLine(tog); // Prints "1"
    Console.WriteLine(tog.Format()); // Prints "On"

    object[] oa = Enum.GetValues(typeof(Toggle));
    foreach(Toggle tog in oa) // Prints "On=1, Off=0"
      Console.WriteLine("{0}={1}", tog.Format(), tog); 
  }
}

Delegates

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);
   }
}

Multicast Delegates

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");
   }
}

Test {
   MethodInvoker m = null;
   m += new MethodInvoker(Foo);
   m -= new MethodInvoker(Foo);
   // m is now null
}

Delegates Compared With Interfaces

interface IFilter {
   bool Filter(string s);
}
class Test {
  class FirstHalfOfAlphabetFilter : IFilter {
    public bool Filter(string s) {
      return ("N".CompareTo(s) > 0);
    }      
  }
  static void Main() {
    FirstHalfOfAlphabetFilter f = new FirstHalfOfAlphabetFilter();
    Display(new string [] {"Ant", "Lion", "Yak"}, f);
  }
  static void Display(string[] names, IFilter f) {
    int count = 0;
    foreach (string s in names)
      if (f.Filter(s))
        Console.WriteLine("Item {0} is {1}", count++, s);
  }
}

Events

Defining a Delegate for an Event

delegate void MoveEventHandler(object source, MoveEventArgs e);

Storing Data for an Event with EventArgs

public class MoveEventArgs : EventArgs {
  public int newPosition;
  public bool cancel;
  public MoveEventArgs(int newPosition) {
    this.newPosition = newPosition;
  }
}

Declaring and Firing an Event

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;
      }
    }  
  }
}

Acting on an Event with Event Handlers

class Form {
  static void Main() {
    Slider slider = new Slider();
    // register with the Move event
    slider.Move += new MoveEventHandler(slider_Move);
    slider.Position = 20;
    slider.Position = 60;
  }
  static void slider_Move(object source, MoveEventArgs e) {
    if(e.newPosition < 50)
      Console.WriteLine("OK");
    else {
      e.cancel = true;
      Console.WriteLine("Can't go that high!");
    }
  }
}

Events As Properties

public event MoveEventHandler Move {
  get {
    return (MoveEventHandler)myEventStorer["Move"];
  }
  set {
    myEventStore ["Move"] = value;
  }
}

Try Statements and Exceptions

Exceptions

public class File {
  ...
  public static StreamWriter CreateText(string s) {
    ...
    if (!Valid(s))      
      throw new IOException("Couldn't create...", ...);
      ...
  }
}
class Test {
  ...
  void Foo(object x) {
    StreamWriter sw = null;
    try {
      sw = File.CreateText("foo.txt");
      sw.Write(x.ToString());
    }
    catch(IOException ex) {
      Console.WriteLine(ex);
    }
    finally {
      if(sw != null)
        sw.Close();
    }
  }
}

Catch Statement

catch(IOException) { // don't specify variable
  Console.WriteLine("Couldn't create the foo!");
}

Catching System.Exception

catch {
  Console.WriteLine("Couldn't create the foo!");
}

Specifying Multiple Catch Clauses

try {...}
catch (NullReferenceException) {...}
catch (IOException) {...}
catch {...}

Attributes

Attribute Classes

[Serializable]
public class Foo {...}

class SerializableAttribute : Attribute {...}

[System.SerializableAttribute]
public class Foo {...}

Named and Positional Parameters

[Obsolete("Use Bar class instead", IsError=true)]
public class Foo {...}

Attribute Targets

[assembly:CLSCompliant(true)]

Specifying Multiple Attributes

[Serializable, Obsolete, CLSCompliant(false)]
public class Bar {...}

[Serializable] 
[Obsolete] 
[CLSCompliant(false)]
public class Bar {...}

[Serializable, Obsolete] 
[CLSCompliant(false)]
public class Bar {...}

Unsafe Code and Pointers

Unsafe Code

unsafe void RedFilter(int[,] bitmap) {
  const int length = bitmap.Length;
  fixed (int* b = bitmap) {
    int* p = b;
    for(int i = 0; i < length; i++)
      *p++ &= 0xFF;
  }
}

Fixed Statement

class Test {
  int x;
  static void Main() {
    Test test = new Test();
    unsafe {
       fixed(int* p = &test.x) { // pins Test
         *p = 9;
       }
       System.Console.WriteLine(test.x);
    }
  }
}

Pointer to Member Operator

struct Test {
   int x;
   unsafe static void Main() {
      Test test = new Test();
      Test* p = &test;
      p->x = 9;
      System.Console.WriteLine(test.x);
   }
}

The stackalloc Keyword

int* a = stackalloc int [10];
for (int i = 0; i < 10; ++i)
   Console.WriteLine(a[i]); // print raw memory

Pre-Processor Directives

#define DEBUG
class MyClass {
  int x;
  void Foo() {
  # if DEBUG
    Console.WriteLine("Testing: x = {0}", x);
  # endif
  ...
}

#define DEBUG = true

XML Documentation

C/C++-Style Comments

int x = 3; // this is a comment
MyMethod(); /* this is a
comment that spans two lines */

Documentation Comments

// Filename: DocTest.cs
using System;
class MyClass {
  /// <summary>
  /// The Foo method is called from
  ///   <see cref="Main">Main</see> 
  /// </summary>
  /// <mytag>Secret stuff</mytag>
  /// <param name="s">Description for s</param>
  static void Foo(string s) { Console.WriteLine(s); }
  static void Main() { Foo("42"); }
}  

XML Documentation Files

<?xml version="1.0"?>
<doc>
  <assembly>
    <name>DocTest</name>
  </assembly>
  <members>
    <member name="M:MyClass.Foo(System.String)">
      <summary>
      The Foo method is called from
        <see cref="M:MyClass.Main">Main</see> 
      </summary>
      <mytag>Secret stuff</mytag>
      <param name="s">Description for s</param>
     </member>
  </members>
</doc> 
Predefined XML Tags

<summary>, <remarks>

<summary>description</summary> <remarks>description</remarks>
<param>
<param name="name">description</param>
<returns>
<returns>description</returns>
<exception>
<exception [cref="type"]>description</exception>
<permission>
<permission [cref="type"]>description </permission>
The C# compiler supports the following XML tags.<example>, <c>, <code>
<example>description</example> <c>code</c> <code>code</code>
<see>, <seealso>
<see cref="member">text</see> <seealso cref="member">text</seealso>
<value>
<value>description</value>
<paramref>
<paramref name="name"/>
<list>, <para>
<list type=[ bullet | number | table ]>
  <listheader>
    <term>name</term>
    <description>description</description>
  </listheader>
  <item>
    <term>name</term>
    <description>description</description>
  </item>
</list>
<para>text</para>

Type or Member Cross-References

// Namespaces do not have independent signatures
namespace NS {
  // T:NS.MyClass
  class MyClass {
    // F:NS.MyClass.aField
    string aField;
    // P:NS.MyClass.aProperty
    short aProperty {get {...} set {...}}
    // T:NS.MyClass.NestedType
    class NestedType {...};
    // M:NS.MyClass.X()
    void X() {...}
    // M:NS.MyClass.Y(System.Int32,System.Double@,System.Decimal@)
    void Y(int p1, ref double p2, out decimal p3) {...}
    // M:NS.MyClass.Z(System.Char[],System.Single[0:,0:])
    void Z(char[] p1, float[,] p2) {...}
    // M:NS.MyClass.op_Addition(NS.MyClass,NS.MyClass)
    public static MyClass operator+(MyClass c1, MyClass c2) {...}
    // M:NS.MyClass.op_Implicit(NS.MyClass)~System.Int32
    public static implicit operator int(MyClass c) {...}
    // M:NS.MyClass.#ctor
    MyClass() {...}
    // M:NS.MyClass.Finalize
    ~MyClass() {...}
    // M:NS.MyClass.#cctor
    static MyClass() {...}
  }
}

Programming the .NET Framework

Common Types

Object Class

public class Object {
  public Object() {...}
  public virtual bool Equals(object o) {...}
  public virtual int GetHashCode(){...}
  public Type GetType(){...}
  public virtual string ToString() {...}
  protected virtual void Finalize() {...}
  protected object MemberwiseClone() {...}
}

using System;
class Beeblebrox {}
class Test {
  static void Main() {
    string s = "Zaphod";
    Beeblebrox b = new Beeblebrox();
    Console.WriteLine(s); // Prints "Zaphod"
    Console.WriteLine(b); // Prints "Beeblebrox"
  }
}

Creating BCL-Friendly Types

public class Point3D {
  public int x, y, z;
  public Point3D(int x, int y, int z) {
    this.x=x; this.y=y; this.z=z; // Initialize data
  }
  public override bool Equals(object o) {
    if (!(o is Point3D)) // Check for type equivalence
      return false;
    return (this==(Point3D)o); // Implemented by operator==
  }
  public static bool operator !=(Point3D lhs, Point3D rhs) {
    return (!(lhs==rhs)); // Implemented by operator==
  }
  public static bool operator ==(Point3D lhs, Point3D rhs) {
    return ((rhs.x==lhs.x) && (rhs.y==lhs.y) && (rhs.z==lhs.z));
  }
  public override int GetHashCode(){
    return x^y^z;
  }
  public override string ToString() {
    return String.Format("[{0},{1},{2}]", x, y, z);
  }
}

using System;
using System.Collections;
public class Point3D {...}
class TestPoint3D {
  static void Main() {
    // Uses ToString, prints "p1=[1,1,1] p2=[2,2,2] p3=[2,2,2]"
    Point3D p1 = new Point3D(1,1,1);
    Point3D p2 = new Point3D(2,2,2);
    Point3D p3 = new Point3D(2,2,2);
    Console.WriteLine("p1={0} p2={1} p3={2}", p1, p2, p3);

    // Tests for equality to demonstrate Equals, == & !=
    int i = 100;
    Console.WriteLine(p1.Equals(i)); // Prints "False"
    Console.WriteLine(p1==p2); // Prints "False"
    Console.WriteLine(p2==p3); // Prints "True"

    // Use a hashtable to store points (uses GetHashCode)
    Hashtable ht = new Hashtable();
    ht["p1"] = p1; 
    ht["p2"] = p2;
    ht["p3"] = p3;

    // Prints "p2=[2,2,2] p3=[2,2,2] p1=[1,1,1]"
    foreach (DictionaryEntry de in ht)
      Console.Write("{0}={1} ", de.Key, de.Value);  
  }
}

ICloneable Interface

public interface ICloneable {
  object Clone();
}

public class A : ICloneable {
   int x;
   public object Clone() {
       return MemberwiseClone();
   }
}

IComparable Interface

interface IComparable {
  int CompareTo(object o);
}

using System;
using System.Collections;
class MyType : IComparable {
  public int x;
  public MyType(int x) {
    this.x = x;
  }
  public int CompareTo(object o) {
    return x -((MyType)o).x;
  }
}
class Test {
  static void Main() {
    ArrayList a = new ArrayList();
    a.Add(new MyType(42));
    a.Add(new MyType(17));
    a.Sort();
    foreach(MyType t in a)
      Console.WriteLine(((MyType)t).x);
   }
}

IFormattable Interface

public interface IFormattable {
  string Format(string format, IServiceObjectProvider sop);
}

Math

Language Support for Math

struct Vector {
  float direction;
  float magnitude;
  public Vector(float direction, float magnitude) {
    this.direction = direction;
    this.magnitude = magnitude;
  }
  public static Vector operator *(Vector v, float scale) {
    return new Vector(v.direction, v.magnitude * scale);
  }
  public static Vector operator /(Vector v, float scale) {
    return new Vector(v.direction, v.magnitude * scale);
  }
  ...
}
class Test {
  static void Main() {
  Vector [,] matrix = {{new Vector(1f,2f), new Vector(6f,2f)},
                       {new Vector(7f,3f), new Vector(4f,9f)}};
  for (int i=0; i<matrix.GetLength(0); i++)
    for (int j=0; j<matrix.GetLength(1); j++)
      matrix[i, j] *= 2f;
}

Special Types and Operators

Math Class

using System;
class Test {
  static void Main() {
    double a = 3;
    double b = 4;
    double C = Math.PI / 2;
    double c = Math.Sqrt (a*a+b*b-2*a*b*Math.Cos(C));
    Console.WriteLine("The length of side c is "+c);
  }
}

Random Class

Random r = new Random();
Console.WriteLine(r.Next(50)); // return between 0 and 50

Strings

Immutability of Strings

string a = "Heat";
string b = a.Insert(3, "r");
Console.WriteLine(b); // Prints Heart
If you need a mutable string, see the StringBuilder class.

String Interning

string a = "hello";
string b = "hello";
Console.WriteLine(a == b); // True
Console.WriteLine(a.Equals(b)); // True
Console.WriteLine((object)a == (object)b); // True!!

Formatting Strings

using System;
class TestFormatting {
  static void Main() {
    int i = 2;
    decimal m = 42.73m;
    string s = String.Format("Account {0} has {1:C}.", i, m);
    Console.WriteLine(s); // Prints "Account 2 has $42.73"
  }
}

Indexing Strings

using System;
class TestIndexing {
  static void Main() {
    string s = "Going down?";
    for (int i=0; i<s.Length; i++)
      Console.WriteLine(s[i]); // Prints s vertically
  }
}

Encoding Strings

using System;
using System.Text;
class TestEncoding {
  static void Main() {
    byte[] ba = new byte[] { 67, 35, 32, 105, 115, 
                             32, 67, 79, 79, 76, 33 };
    string s = Encoding.ASCII.GetString(ba);
    Console.WriteLine(s);
  }
} 

StringBuilder Class

using System;
using System.Text;
class TestStringBuilder {
  static void Main() {
    StringBuilder sb = new StringBuilder("Hello, ");
    sb.Append("World");
    sb[11] = '!';
    Console.WriteLine(sb); // Hello, World!
  }
}

Collections

ArrayList Class

ArrayList a = new ArrayList();
a.Add("Vernon");
a.Add("Corey");
a.Add("William");
a.Add("Muzz");
a.Sort();
for(int i = 0; i < a.Count; i++)
   Console.WriteLine(a [i]);

BitArray Class

BitArray bits = new BitArray();
bits.Length = 2;
bits[1] = true;
bits.Xor(bits); // Xor the array with itself

Hashtable Class

Hashtable ht = new Hashtable();
ht["One"] = 1;
ht["Two"] = 2;
ht["Three"] = 3;
Console.WriteLine(ht["Two"]); // Prints "2"

Queue Class

Queue q = new Queue();
q.Enqueue(1);
q.Enqueue(2);
Console.WriteLine(q.Dequeue()); // Prints "1"
Console.WriteLine(q.Dequeue()); // Prints "2"

SortedList Class

SortedList s = new SortedList();
s["Zebra"] = 1;
s["Antelope"] = 2;
s["Eland"] = 3;
s["Giraffe"] = 4;
s["Meerkat"] = 5;
s["Dassie"] = 6;
s["Tokoloshe"] = 7;
Console.WriteLine(s["Meerkat"]); // Prints "5" in 3 lookups
Stack Class

Stack s = new Stack();
s.Push(1); // Stack = 1
s.Push(2); // Stack = 1,2
s.Push(3); // Stack = 1,2,3
Console.WriteLine(s.Pop()); // Prints 3, Stack=1,2
Console.WriteLine(s.Pop()); // Prints 2, Stack=1
Console.WriteLine(s.Pop()); // Prints 1, Stack=

StringCollection Class

StringCollection sc = new StringCollection();
sc.Add("s1");
string[] sarr =  {"s2", "s3", "s4"};
sc.AddRange(sarr);
foreach (string s in sc)
  Console.Write("{0} ", s); // s1 s2 s3 s4

Collection Interfaces

IEnumerable
public interface IEnumerable {
  IEnumerator GetEnumerator();
}

IEnumerator

public interface IEnumerator {
   bool MoveNext();
   object Current {get;}
   void Reset();
}

using System.Collections;
public class MyCollection : IEnumerable {
  // ...
  public virtual IEnumerator GetEnumerator () {
    return new MyCollection.Enumerator(this);
  }
  private class Enumerator : IEnumerator { 
    private MyCollection collection;
    private int currentIndex = -1;

    internal Enumerator (MyCollection collection) {
      this.collection = collection;
    }
    public object Current {
      get {
        if (currentIndex == collection.Count)
          throw new InvalidOperationException();
        return collection [currentIndex];
      }
    }
    public bool MoveNext () {
      if (currentIndex > collection.Count)
        throw new InvalidOperationException();
      return ++currentIndex < collection.Count;
    }
    public void Reset () {
      currentIndex = -1;
    }
  }
}

MyCollection mcoll = new MyCollection();
...
// Using foreach: substitute your typename for XXX
foreach (XXX item in mcoll) {
  Console.WriteLine(item);
  ...
}
// Using IEnumerator: substitute your typename for XXX
IEnumerator ie = myc.GetEnumerator();
while (myc.MoveNext()) {
  XXX item = (XXX)myc.Current;
  Console.WriteLine(item);
  ...
}

ICollection Interface

public interface ICollection : IEnumerable {
   void CopyTo(Array array, int index);
   int Count {get;}
   bool IsReadOnly {get;}
   bool IsSynchronized {get;}
   object SyncRoot {get;}
}
IComparer Interface
public interface IComparer {
   int Compare(object x, object y);
}

IList Interface

public interface IList : ICollection, IEnumerable {
   object this [int index] {get; set}
   int Add(object o);
   void Clear();
   bool Contains(object value);
   int IndexOf(object value);
   void Insert(int index, object value);
   void Remove(object value);
   void RemoveAt(int index);
}

IDictionary Interface

public interface IDictionary : ICollection, IEnumerable {
   object this [object key] {get; set};
   ICollection Keys {get;}
   ICollection Values {get;}
   void Clear();
   bool Contains(object value);
   IDictionaryEnumerator GetEnumerator();
   void Remove(object key);
}

IDictionaryEnumerator Interface

public interface IDictionaryEnumerator : IEnumerator {
   DictionaryEntry Entry {get;}
   object Key {get;}
   object Value {get;}
}

IHashCodeProvider Interface

public interface IHashCodeProvider {
   int GetHashCode(object o);
}

Regular Expressions

Using Regular Expressions

// RegEx.cs - compile with /r:System.Text.RegularExpressions.dll
using System;
using System.Text.RegularExpressions;
class TestRegEx {
  static void Main() {
    string pat =
@"(?<1>[^\+!?#]*)(?<2>\+)?[^!?#]*?(?<3>#)?(?<4>[!?]?!|\?)?(?<3>#)?";

    string t = "e8=Q+#!!";
    Regex regex = new Regex(pat);
    Match m = regex.Match(t);
    if(m == null)
      System.Console.WriteLine("No Match");
    else
      System.Console.Write(
        "Group 1 = "+ m.Group(1).ToString() + "\n" +
        "Group 2 = "+ m.Group(2).ToString() + "\n" +
        "Group 3 = "+ m.Group(3).ToString() + "\n" +
        "Group 4 = "+ m.Group(4).ToString() + "\n");
  }
}

Group 1 = e8=Q
Group 2 = +
Group 3 = #
Group 4 = !!

Input/Output

Concrete Stream-Derived Classes

using System.IO;
class Test {
  static void Main() {
    Stream s = new FileStream("foo.txt", Filemode.Create);
    s.WriteByte("67");
    s.WriteByte("35");
    s.Close();
  }
}

StreamReader and StreamWriter Classes

using System.Text;
using System.IO;
class Test {
  static void Main() {
    Stream fs = new FileStream ("foo.txt", FileMode.Create);
    StreamWriter sw = new StreamWriter(fs, Encoding.ASCII);
    sw.Write("Hello!");
    sw.Close();
  }
}

StringReader and StringWriter Classes

using System;
using System.IO;
using System.Text;
class Test {
  static void Main() {
    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    WriteHello(sw);
    Console.WriteLine(sb);
  }
  static void WriteHello(TextWriter tw) {
    tw.Write("Hello, String I/O!");
  }
}

Directories and Files

using System;
using System.IO;
class Test {
   static void Main(string[] args) {
      Stream s = File.OpenRead(args[0]);
      StreamReader sr = new StreamReader(s);
      Console.WriteLine(sr.ReadLine());
      sr.Close();
   }
}

Networking

Generic Request/Response Architecture

// Snarf.cs - compile with /r:System.Net.dll
// Run Snarf.exe <http-uri> to retrieve a web page
using System;
using System.IO;
using System.Net;
using System.Text;
class Snarf {
  static void Main(string[] args) {

    // Retrieve the data at the URL with an WebRequest ABC
    WebRequest req = WebRequestFactory.Create(args[0]);
    WebResponse resp = req.GetResponse();

    // Read in the data, performing ASCII->Unicode encoding
    Stream s = resp.GetResponseStream();
    StreamReader sr = new StreamReader(s, Encoding.ASCII);
    string doc = sr.ReadToEnd();

    Console.WriteLine(doc); // Print result to console
  }
}

HTTP-Specific Support

// ProbeSvr.cs - compile with /r:System.Net.dll
// Run ProbeSvr.exe <servername> to retrieve the server type
using System;
using System.Net;
class ProbeSvr {
  static void Main(string[] args) {

    // Get instance of WebRequest ABC, convert to HttpWebRequest
    WebRequest req = WebRequestFactory.Create(args[0]);
    HttpWebRequest httpReq = (HttpWebRequest)req;

    // Access HTTP-specific features such as User-Agent
    httpReq.UserAgent = "CSPRProbe/1.0";

    // Retrieve response and print to console
    WebResponse resp = req.GetResponse();
    HttpWebResponse httpResp = (HttpWebResponse)resp;
    Console.WriteLine(httpResp.Server);
  }
}

// QOTDListener.cs - compile with /r:System.Net.dll 
// Run QOTDListener.exe to service incoming QOTD requests
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class QOTDListener {
  static string[] quotes = 
{@"Sufficiently advanced magic is indistinguishable from technology -- Terry Pratchett",
 @"Sufficiently advanced technology is indistinguishable from magic -- Arthur C Clarke" };
  static void Main() {

    // Start a TCP listener on port 17
    TCPListener l = new TCPListener(17);
    l.Start();
    Console.WriteLine("Waiting for clients to connect");
    Console.WriteLine("Press Ctrl+C to quit...");
    int numServed = 1;
    while (true) {

      // Block waiting for an incoming socket connect request
      Socket s = l.Accept();

      // Encode alternating quotes as bytes for sending 
      Char[] carr = quotes[numServed%2].ToCharArray();
      Byte[] barr = Encoding.ASCII.GetBytes(carr);

      // Return data to client, then clean up socket & repeat
      s.Send(barr, barr.Length, 0);
      s.Shutdown(SocketShutdown.SdBoth);
      s.Close();
      Console.WriteLine("{0} quotes served...", numServed++);
    }
  }
}

Using DNS

// DNSLookup.cs - compile with /r:System.Net.dll
// Run DNSLookup.exe <servername> to determine IP addresses
using System;
using System.Net;
class DNSLookup {
  static void Main(string[] args) {
    IPHostEntry he = DNS.GetHostByName(args[0]);
    IPAddress[] addrs = he.AddressList;
    foreach (IPAddress addr in addrs)
      Console.WriteLine(addr);
  }
}

Threading

using System;
using System.Threading;
class ThreadTest {
  static void Main() {
  Thread t = new Thread(new ThreadStart(Go));
    t.Start();
    Go();
  }
  static void Go() {
    for (char c='a'; c<='z'; c++ )
      Console.Write(c);
  }
}

abcdabcdefghijklmnopqrsefghjiklmnopqrstuvwxyztuvwxyz

Thread Synchronization

The Lock Statement

using System;
using System.Threading;
class LockTest {
  static void Main() {
    LockTest lt = new LockTest ();
    Thread t = new Thread(new ThreadStart(lt.Go));
    t.Start();
    lt.Go();
  }
  void Go() {
    lock(this)
      for ( char c='a'; c<='z'; c++)
        Console.Write(c);
  }
}

abcdefghijklmnopqrstuvwzyzabcdefghijklmnopqrstuvwzyz

System.Threading.Monitor.Enter(expression);
try {
  ...
}
finally {
  System.Threading.Monitor.Exit(expression);
}

Pulse and Wait

using System;
using System.Threading;
class MonitorTest {
  static void Main() {
    MonitorTest mt = new MonitorTest();
    Thread t = new Thread(new ThreadStart(mt.Go));
    t.Start();
    mt.Go();
  }
  void Go() {
    for ( char c='a'; c<='z'; c++)
      lock(this) {
        Console.Write(c);
        Monitor.Pulse(this);
        Monitor.Wait(this);
      }
  }
}

aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz

Deadlocks

  void Go() {
    for ( char c='a'; c<='z'; c++)
      lock(this) {
        Console.Write(c);
        Monitor.Pulse(this);
        if (c<'z')
          Monitor.Wait(this);
      }
  }

Reflection

Retrieving a Type Directly

Type t = Type.GetType("System.Int32");

Type t = typeof(System.Int32);

Reflecting Over a Type Hierarchy

using System;
using System.Reflection;
class Test {
  static void Main() {
    object o = new Object();
    DumpTypeInfo(o.GetType());
    DumpTypeInfo(typeof(int));
    DumpTypeInfo(Type.GetType("System.String"));
  }
  static void DumpTypeInfo(Type t) {
    Console.WriteLine("Type: {0}", t);

    // Retrieve the list of members in the type
    MemberInfo[] miarr = t.GetMembers(BindingFlags.LookupAll);

    // Print out details on each of them
    foreach (MemberInfo mi in miarr)
      Console.WriteLine("  {0}={1}", mi.MemberType.Format(), mi);
  }
}

Late Binding to Types

// Greeting.cs - compile with /t:library
public abstract class Greeting { 
  public abstract void SayHello();
}

// English.cs - compile with /t:library /r:Greeting.dll
using System;
public class AmericanGreeting : Greeting {
  private string msg = "Hey, dude. Wassup!";
  public override void SayHello() {
    Console.WriteLine(msg);
  }
}
public class BritishGreeting : Greeting {
  private string msg = "Good morning, old chap!";
  public override void SayHello() {
    Console.WriteLine(msg);
  }
}

// SayHello.cs - compile with /r:Greeting.dll
// Run with SayHello.exe <dllname1> <dllname2> ... <dllnameN>
using System;
using System.Reflection;
class Test {
  static void Main (string[] args) {

    // Iterate over the cmd-line options,
    // trying to load each assembly
    foreach (string s in args) {
      Assembly a = Assembly.LoadFrom(s);
      
      // Pick through all the public type, looking for
      // subtypes of the abstract base class Greeting
      foreach (Type t in a.GetTypes())
        if (t.IsSubclassOf(typeof(Greeting))) {

          // Having found an appropriate subtype, create it
          object o = Activator.CreateInstance(t);

          // Retrieve the SayHello MethodInfo & invoke it
          MethodInfo mi = t.GetMethod("SayHello");
          mi.Invoke(o, null);
        }
    }
  }
}

Hey, dude. Wassup!
Good morning, old chap!

Forms of Activation

object o = Activator.CreateInstance("Assem1.dll",              
                                    "Friendly.Greeting");

Advanced Uses of Reflection

// InControl.cs - compile with /r:Greeting.dll,English.dll
using System;
using System.Reflection;
class TestReflection {
  // Note: This method requires the ReflectionPermission perm.
  static void ModifyPrivateData(object o, string msg) {

    // Get a FieldInfo type for the private data member
    Type t = o.GetType(); 
    FieldInfo fi = t.GetField("msg", BindingFlags.NonPublic|
                                     BindingFlags.Instance);

    // Use the FieldInfo to adjust the data member value
    fi.SetValue(o, msg);
  }
  static void Main() {
    // Create instances of both types
    BritishGreeting bg = new BritishGreeting();
    AmericanGreeting ag = new AmericanGreeting();

    // Adjust the private data via reflection
    ModifyPrivateData(ag, "Things are not the way they seem");
    ModifyPrivateData(bg, "The runtime is in total control!");
    
    // Display the modified greeting strings
    ag.SayHello(); // "Things are not the way they seem"
    bg.SayHello(); // "The runtime is in total control!"
  }
}

Things are not the way they seem
The runtime is in total control!

Creating New Types at Runtime

using System;
using System.Reflection;
using System.Reflection.Emit;
public class Test
{
  static void Main()
  {
    // Create a dynamic assembly in the current AppDomain
    AppDomain ad = AppDomain.CurrentDomain;
    AssemblyName an = new AssemblyName();
    an.Name = "DynAssembly";
    AssemblyBuilder ab = 
      ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
    
    // Create a module in the assembly & a type in the module
    Assembly a = (Assembly)ab;
    ModuleBuilder modb = a.DefineDynamicModule("DynModule");
    TypeBuilder tb = modb.DefineType("AgentSmith", 
                                     TypeAttributes.Public);
 
    // Add a SayHello member to the type 
    MethodBuilder mb = tb.DefineMethod("SayHello",        
                                       MethodAttributes.Public,
                                       null, null);
                                        
    // Generate the MSIL for the SayHello Member
    ILGenerator ilg = mb.GetILGenerator();
    ilg.EmitWriteLine("Never send a human to do a machine's job.");
    ilg.Emit(OpCodes.Ret);

    // Finalize the type so we can create it
    tb.CreateType();

    // Create an instance of the new type
    Type t = Type.GetType("AgentSmith");
    object o = Activator.CreateInstance(t);
    
    // Prints "Never send a human to do a machine's job."
    t.GetMethod("SayHello").Invoke(o, null);
  }
}

Custom Attributes

Defining a New Custom Attribute

using System;
[AttributeUsage(AttributeTargets.ClassMembers, AllowMultiple=true)]
class CrossRefAttribute : Attribute {
  Type   xref;
  string desc = "";
  public string Description { set { desc=value; } } 
  public CrossRefAttribute(Type xref) { this.xref=xref; }
  public override string ToString() {
    string tmp = (desc.Length>0) ? " ("+desc+")" : "";
    return "CrossRef to "+xref.ToString()+tmp;
  }			
}

[CrossRef(typeof(Bar), Description="Foos often hang around Bars")]
class Foo {...}

Retrieving a Custom Attribute at Runtime

using System;
[Serializable, Obsolete]
class Test {
  static void Main() {
    Type t = typeof(Test);
    object[] caarr = t.GetCustomAttributes();
    Console.WriteLine("{0} has {1} custom attribute(s)",
                      t, caarr.Length);
    foreach (object ca in caarr)
      Console.WriteLine(ca);
  }
}

Test has 1 custom attribute(s)
System.ObsoleteAttribute

Dispose and Close Methods

public class Worker {
  ...
  public void Dispose() {
    // Perform normal cleanup
    ... 
    // Mark this object finalized
    GC.SuppressFinalize(this);
  }
  protected override void Finalize() {
    Dispose();
    base.Finalize();
  }
}

Interop With Native DLLs

int MessageBox(HWND hWnd, LPCTSTR lpText, 
               LPCTSTR lpCation, UINT uType);

using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern int MessageBox(int hWnd, string text, 
                             string caption, int type);
Marshaling Common Types

using System.Runtime.InteropServices;
static extern int Foo([MarshalAs(UnmanagedType.LPStr)]
                      string s);

using System.Runtime.InteropServices;
[DllImport("kernel32.dll")]
static extern int GetWindowsDirectory(StringBuilder sb,
                                      int maxChars);
class Test {
   static void Main() {
      StringBuilder s = new String(256);
      GetWindowsDirectory(s, 256);
      Console.WriteLine(s);
   }
}

Marshalling Classes and Structs

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
class SystemTime {
   public ushort wYear; 
   public ushort wMonth;
   public ushort wDayOfWeek; 
   public ushort wDay; 
   public ushort wHour; 
   public ushort wMinute; 
   public ushort wSecond; 
   public ushort wMilliseconds; 
}
class Test {
   [DllImport("kernel32.dll")]
   static extern void GetSystemTime(SystemTime t);
   static void Main() {
      SystemTime t = new SystemTime();
      GetSystemTime(t);
      Console.WriteLine(t.wYear);
   }
}

In and Out Marshaling

struct SystemTime {...}
static extern void GetSystemTime(ref SystemTime t);

static extern void Foo([in] int[] array);

Callbacks from Unmanaged Code

class Test {
   delegate bool CallBack(int hWnd, int lParam);
   [DllImport("user32.dll")]
   static extern int EnumWindows(CallBack hWnd, int lParam);
   static bool PrintWindow(int hWnd, int lParam) {
      Console.WriteLine(hWnd);
      return true;
   }
   static void Main() {
      CallBack e = new CallBack(PrintWindow);
      EnumWindows(e, 0);
   }
}

Interop With COM

Exposing COM Objects To C#

// IMAdd.cs - compile with /r:Messenger.dll
// Run IMAdd.exe <UserID> to add an MSN Instant 
//   Messenger user to Contacts
// Run TlbImp.exe "C:\Program Files\Messenger\msmsgs.exe" 
//   to create Messenger.dll
using System.Runtime.InteropServices;
using Messenger; // COM API for MSN Instant Messenger
class COMConsumer {
   static void Main(string[] args) {
      MessengerApp m = new MessengerApp()
      m.LaunchAddContactUI(args[0]);
   }
}

Exposing C# Objects To COM

[GuidAttribute("aa6b10a2-dc4f-4a24-ae5e-90362c2142c1")]
public interface : IRunInfo {
  [DispId(1)]
  string GetRunInfo();
}
[GuidAttribute("b72ccf55-88cc-4657-8577-72bd0ff767bc")]
public class StackSnapshot : IRunInfo {
  public StackSnapshot() {
    st = new StackTrace();
  }
  [DispId(1)]
  public string GetRunInfo() {
    return st.ToString();
  }
  private StackTrace st;
}

Regular Expressions

using System;
class TestDefaultFormats {
  static void Main() {
    int i = 654321;
    Console.WriteLine("{0:C}", i); // $654,321.00
    Console.WriteLine("{0:D}", i); // 654321
    Console.WriteLine("{0:E}", i); // 6.543210E+005
    Console.WriteLine("{0:F}", i); // 654321.00
    Console.WriteLine("{0:G}", i); // 654321
    Console.WriteLine("{0:N}", i); // 654,321.00
    Console.WriteLine("{0:X}", i); // 9FBF1
    Console.WriteLine("{0:x}", i); // 9fbf1
  }
}

using System;
class TestIntegerFormats {
  static void Main() {
    int i = 123;
    Console.WriteLine("{0:C6}", i); // $123.000000
    Console.WriteLine("{0:D6}", i); // 000123
    Console.WriteLine("{0:E6}", i); // 1.230000E+002
    Console.WriteLine("{0:G6}", i); // 123
    Console.WriteLine("{0:N6}", i); // 123.000000
    Console.WriteLine("{0:X6}", i); // 00007B
    i = -123;
    Console.WriteLine("{0:C6}", i); // ($123.000000)
    Console.WriteLine("{0:D6}", i); // -000123
    Console.WriteLine("{0:E6}", i); // -1.230000E+002
    Console.WriteLine("{0:G6}", i); // -123
    Console.WriteLine("{0:N6}", i); // -123.000000
    Console.WriteLine("{0:X6}", i); // FFFF85
    i = 0;
    Console.WriteLine("{0:C6}", i); // $0.000000
    Console.WriteLine("{0:D6}", i); // 000000
    Console.WriteLine("{0:E6}", i); // 0.000000E+000
    Console.WriteLine("{0:G6}", i); // 0
    Console.WriteLine("{0:N6}", i); // 0.000000
    Console.WriteLine("{0:X6}", i); // 000000
  }
}                                                

using System;
class TestDoubleFormats {
  static void Main() {
    double d = 1.23;
    Console.WriteLine("{0:C6}", d); // $1.230000
    Console.WriteLine("{0:E6}", d); // 1.230000E+000
    Console.WriteLine("{0:G6}", d); // 1.23
    Console.WriteLine("{0:N6}", d); // 1.230000
    d = -1.23;
    Console.WriteLine("{0:C6}", d); // ($1.230000)
    Console.WriteLine("{0:E6}", d); // -1.230000E+000
    Console.WriteLine("{0:G6}", d); // -1.23
    Console.WriteLine("{0:N6}", d); // -1.230000
    d = 0;
    Console.WriteLine("{0:C6}", d); // $0.000000
    Console.WriteLine("{0:E6}", d); // 0.000000E+000
    Console.WriteLine("{0:G6}", d); // 0
    Console.WriteLine("{0:N6}", d); // 0.000000
  }
}

using System;
class TestIntegerCustomFormats {
  static void Main() {
    int i = 123;
    Console.WriteLine("{0:#0}", i);             // 123
    Console.WriteLine("{0:#0;(#0)}", i);        // 123
    Console.WriteLine("{0:#0;(#0);<zero>}", i); // 123
    Console.WriteLine("{0:#%}", i);             // 12300%
    i = -123;
    Console.WriteLine("{0:#0}", i);             // -123
    Console.WriteLine("{0:#0;(#0)}", i);        // (123)
    Console.WriteLine("{0:#0;(#0);<zero>}", i); // (123)
    Console.WriteLine("{0:#%}", i);             // -12300%
    i = 0;
    Console.WriteLine("{0:#0}", i);             // 0
    Console.WriteLine("{0:#0;(#0)}", i);        // 0
    Console.WriteLine("{0:#0;(#0);<zero>}", i); // <zero>
    Console.WriteLine("{0:#%}", i);             // %
  }
}

using System;
class TestDoubleCustomFormats {
  static void Main() {
    double d = 1.23;
    Console.WriteLine("{0:#.000E+00}", d);    // 1.230E+00
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00)}", d);        // 1.230E+00
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00);<zero>}", d); // 1.230E+00
    Console.WriteLine("{0:#%}", d);           // 123%
    d = -1.23;
    Console.WriteLine("{0:#.000E+00}", d);    // -1.230E+00
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00)}", d);        // (1.230E+00)
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00);<zero>}", d); // (1.230E+00)
    Console.WriteLine("{0:#%}", d);          // -123%
    d = 0;
    Console.WriteLine("{0:#.000E+00}", d);    // 0.000E-01
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00)}", d);        // 0.000E-01
    Console.WriteLine(
      "{0:#.000E+00;(#.000E+00);<zero>}", d); // <zero>
    Console.WriteLine("{0:#%}", d);           // %
  }
}

DateTime Format Specifiers

using System;
class TestDateTimeFormats {
  static void Main() {
    DateTime dt = new DateTime(2000, 10, 11, 15, 32, 14);
    // Prints "2000-10-11T15:32:14"
    Console.WriteLine(dt.ToString()); 
    // Prints "Wednesday, October 11, 2000"
    Console.WriteLine("{0}", dt);     
    // Prints "10/11/2000"
    Console.WriteLine("{0:d}", dt); 
    // Prints "Wednesday, October 11, 2000"
    Console.WriteLine("{0:D}", dt); 
    // Prints "Wednesday, October 11, 2000 3:32 PM"
    Console.WriteLine("{0:f}", dt); 
    // Prints "Wednesday, October 11, 2000 3:32:14 PM"
    Console.WriteLine("{0:F}", dt);	
    // Prints "10/11/2000 3:32 PM"
    Console.WriteLine("{0:g}", dt); 
    // Prints "10/11/2000 3:32:14 PM"
    Console.WriteLine("{0:G}", dt);	
    // Prints "October 11"
    Console.WriteLine("{0:m}", dt);	
    // Prints "October 11"
    Console.WriteLine("{0:M}", dt);	
    // Prints "Wed, 11 Oct 2000 22:32:14 GMT"
    Console.WriteLine("{0:r}", dt); 
    // Prints "Wed, 11 Oct 2000 22:32:14 GMT"
    Console.WriteLine("{0:R}", dt); 
    // Prints "3:32 PM"
    Console.WriteLine("{0:t}", dt); 
    // Prints "3:32:14 PM"
    Console.WriteLine("{0:T}", dt); 
    // Prints "2000-10-11 22:32:14Z"
    Console.WriteLine("{0:u}", dt); 
    // Prints "Wednesday, October 11, 2000 10:32:14 PM"
    Console.WriteLine("{0:U}", dt); 
    // Prints "October, 2000"
    Console.WriteLine("{0:y}", dt); 
    // Prints "October, 2000"
    Console.WriteLine("{0:Y}", dt); 
    // Prints "Wednesday the 11 day of October in the year 2000"
    Console.WriteLine(
      "{0:dddd 'the' d 'day of' MMMM 'in the year' yyyy}", dt);
  }
}

Using nmake

REF=/r:c.dll
DEBUG=/debug
.SUFFIXES: .exe .dll .cs
.cs.dll:
	csc /t:module $*.cs
.cs.exe:
	csc $(DEBUG) $(REF) @<<big.tmp
$*.cs $(SRCLIST)
<<
all : d.exe f.exe
d.exe : d.cs c.dll
c.dll : a.dll b.dll
	al /out:c.dll a.dll b.dll
b.dll : b.cs
a.dll : a.cs
key.snk :
	sn -k $*.snk
e.dll : a.dll b.dll key.snk
	al /out:$*.dll /keyfile:key.snk a.dll b.dll
	al /i:$*.dll
f.exe : f.cs e.dll
	csc $(DEBUG) /r:.\e.dll f.cs
clean:
	del a.dll b.dll c.dll d.exe /q

[ Back to: C# Essentials ]


oreilly.com Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies | Privacy Policy

© 2001, O'Reilly & Associates, Inc.