Code Listings

Chapter 3: Creating Types in C#

Overloading Constructors:

using System;

public class Wine
{
  public decimal Price;
  public int Year;
  public Wine (decimal price) { Price = price; }
  public Wine (decimal price, int year) : this (price) { Year = year; }
}

Object Initializers:

public class Bunny
{
  public string Name;
  public bool LikesCarrots;
  public bool LikesHumans;

  public Bunny () {}
  public Bunny (string n) { Name = n; }
}
Bunny b1 = new Bunny { Name="Bo", LikesCarrots=true, LikesHumans=false };
Bunny b2 = new Bunny ("Bo")     { LikesCarrots=true, LikesHumans=false };

The this reference:

public class Panda
{
  public Panda Mate;

  public void Marry (Panda partner)
  {
    Mate = partner;
    partner.Mate = this;
  }
}
public class Test
{
  string name;
  public Test (string name) { this.name = name; }
}

Properties:

public class Stock
{
  decimal currentPrice;           // The private "backing" field

  public decimal CurrentPrice     // The public property
  {
     get { return currentPrice; } set { currentPrice = value; }
  }
}

Read-only and calculated properties:

public class Stock
{
  string  symbol;
  decimal purchasePrice, currentPrice;
  long    sharesOwned;

  public Stock (string symbol, decimal purchasePrice, long sharesOwned)
  {
    this.symbol = symbol;
    this.purchasePrice = currentPrice = purchasePrice;
    this.sharesOwned = sharesOwned;
  }

  public decimal CurrentPrice  { get { return currentPrice;             }
                                 set { currentPrice = value;            } }
  public string Symbol         { get { return symbol;                   } }
  public decimal PurchasePrice { get { return purchasePrice;            } }
  public long    SharesOwned   { get { return sharesOwned;              } }
  public decimal Worth         { get { return CurrentPrice*SharesOwned; } }
}

class Test
{
  static void Main()
  {
    Stock msft = new Stock ("MSFT", 20, 1000);
    Console.WriteLine (msft.Worth);                // 20000
    msft.CurrentPrice = 30;
    Console.WriteLine (msft.Worth);                // 30000
  }
}

Automatic properties:

public class Stock
{
  // ...
  public decimal CurrentPrice { get; set; }
}

get and set accessibility:

public class Foo
{
  private decimal x;
  public decimal X
  {
    get          {return x;} 
    internal set {x = value;}
  }
}

Implementing an Indexer:

public class Portfolio
{
  Stock[] stocks;
  public Portfolio (int numberOfStocks)
  {
    stocks = new Stock [numberOfStocks];
  }

  public int NumberOfStocks { get { return stocks.Length; } }

  public Stock this [int index]      // indexer
  { 
    get { return stocks [index];  }
    set { stocks [index] = value; }
  }
}

class Test
{
  static void Main()
  {
    Portfolio portfolio = new Portfolio(3);
    portfolio [0] = new Stock ("MSFT", 20, 1000);
    portfolio [1] = new Stock ("GOOG", 300, 100);
    portfolio [2] = new Stock ("EBAY", 33, 77);

    for (int i = 0; i < portfolio.NumberOfStocks; i++)
      Console.WriteLine (portfolio[i].Symbol);
  }
}

Multiple indexers:

public class Portfolio
{
  ...
  public Stock this[string symbol]
  {
    get
    {
      foreach (Stock s in stocks)
        if (s.Symbol == symbol)
          return s;
      return null;
    }
  }
}

Static Constructors:

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

Partial methods:

// PaymentFormGen.cs — auto-generated
partial class PaymentForm
{
  // ...
  partial void ValidatePayment(decimal amount);
}
// PaymentForm.cs — hand-authored
partial class PaymentForm
{
  // ...
  // partial void ValidatePayment(decimal amount)
  {
    if (amount > 100)
    {
      // ...
    }
  }
}

Inheritance:

public class Asset
{
  public string  Name;
  public decimal PurchasePrice, CurrentPrice;
}
public class Stock : Asset   // inherits from Asset
{
  public long SharesOwned;
}

public class House : Asset   // inherits from Asset
{
  public decimal Mortgage;
}

class Test
{
  static void Main()
  {
    Stock msft = new Stock()
    { Name="MSFT", PurchasePrice=20, CurrentPrice=30, SharesOwned=1000 };

    House mansion = new House
    { Name="McMansion", PurchasePrice=300000, CurrentPrice=200000,
      Mortgage=250000 };

    Console.WriteLine (msft.Name);           // MSFT
    Console.WriteLine (mansion.Name);        // McMansion

    Console.WriteLine (msft.SharesOwned);    // 1000
    Console.WriteLine (mansion.Mortgage);    // 250000
  }
}

Polymorphism:

class Test
{
  static void Main()
  {
    Stock msft    = new Stock ... ;
    House mansion = new House ... ;
    Display (msft);
    Display (mansion);
  }

  public static void Display (Asset asset)
  {
    System.Console.WriteLine (asset.Name);
  }
}
static void Main() { Display (new Asset()); }    // Compile-time error

public static void Display (House house)         // Will not accept Asset
{
  System.Console.WriteLine (house.Mortgage);
}

Downcasting:

Stock msft = new Stock();
Asset a = msft;                      // upcast
Stock s = (Stock)a;                  // downcast
Console.WriteLine (s.SharesOwned);   // <No error>
Console.WriteLine (s == a);          // true
Console.WriteLine (s == msft);       // true

Virtual function members:

public class Asset
{
  ...
  public virtual decimal Liability { get { return 0; } }
}
public class Stock : Asset { ... }

public class House : Asset
{
  ...
  public override decimal Liability { get { return Mortgage; } }
}
House mansion = new House
 { Name="McMansion", PurchasePrice=300000, CurrentPrice=200000,
   Mortgage=250000 };

Asset a = mansion;
decimal d2 = mansion.Liability;      // 250000

Abstract classes & abstract members:

public abstract class Asset
{
  ...
  public abstract decimal NetValue { get; }   // Note empty implementation
}

public class Stock : Asset
{
  ...                                     // Override an abstract method
  public override decimal NetValue        // just like a virtual method.
  {
    get { return CurrentPrice * SharesOwned; }
  }
}

public class House : Asset     // Every non abstract subtype must
{                              // define NetValue.
  ...
  public override decimal NetValue
  {
    get { return CurrentPrice - Mortgage; }
  }
}

new vs. virtual

public class BaseClass
{
  public virtual void Foo()  { Console.WriteLine ("BaseClass.Foo"); }
}

public class Overrider : BaseClass
{
  public override void Foo() { Console.WriteLine ("Overrider.Foo"); }
}

public class Hider : BaseClass
{
  public new void Foo()      { Console.WriteLine ("Hider.Foo"); }
}
Overrider o = new Overrider();
BaseClass b1 = o;
o.Foo();                           // Overrider.Foo
b1.Foo();                          // Overrider.Foo

Hider h = new Hider();
BaseClass b2 = h;
h.Foo();                           // Hider.Foo
b2.Foo();                          // BaseClass.Foo

GetType() and typeof

using System;

public class Point {public int X, Y;}

class Test
{
  static void Main()
  {
    Point p = new Point();
    Console.WriteLine (p.GetType().Name);             // Point
    Console.WriteLine (typeof (Point).Name);          // Point
    Console.WriteLine (p.GetType() == typeof(Point)); // True
    Console.WriteLine (p.X.GetType().Name);           // Int32
    Console.WriteLine (p.Y.GetType().FullName);       // System.Int32
  }
}

Explicit interface implementation:

interface I1 { void Foo(); }
interface I2 { int Foo(); }

public class Widget : I1, I2
{
  public void Foo ()
  {
    Console.WriteLine ("Widget's implementation of I1.Foo");
  }

  int I2.Foo ()
  {
    Console.WriteLine ("Widget's implementation of I2.Foo");
    return 42;
  }
}
Widget w = new Widget();
w.Foo();                      // Widget's implementation of I1.Foo
((I1)w).Foo();                // Widget's implementation of I1.Foo 
((I2)w).Foo();                // Widget's implementation of I2.Foo

Implementing interface members virtually:

public interface IUndoable { void Undo(); }

public class TextBox : IUndoable
{
  public virtual void Undo()
  {
     Console.WriteLine ("TextBox.Undo");
  }
}

public class RichTextBox : TextBox
{
  public override void Undo()
  {
    Console.WriteLine ("RichTextBox.Undo");
  }
}
RichTextBox r = new RichTextBox();
r.Undo();                          // RichTextBox.Undo
((IUndoable)r).Undo();             // RichTextBox.Undo
((TextBox)r).Undo();               // RichTextBox.Undo

Reimplementing an interface in a subclass:

public interface IUndoable { void Undo(); }

public class TextBox : IUndoable
{
  void IUndoable.Undo() { Console.WriteLine ("TextBox.Undo"); }
}

public class RichTextBox : TextBox, IUndoable
{
  public new void Undo() { Console.WriteLine ("RichTextBox.Undo"); }
}
RichTextBox r = new RichTextBox();
r.Undo();                 // RichTextBox.Undo      Case 1
((IUndoable)r).Undo();    // RichTextBox.Undo      Case 2
public class TextBox : IUndoable
{
  public void Undo() { Console.WriteLine ("TextBox.Undo"); }
}
RichTextBox r = new RichTextBox();
r.Undo();                 // RichTextBox.Undo      Case 1
((IUndoable)r).Undo();    // RichTextBox.Undo      Case 2
((TextBox)r).Undo();      // TextBox.Undo          Case 3

Alternatives to interface reimplementation:

public class TextBox : IUndoable
{
  void IUndoable.Undo()         { Undo(); }   // Calls method below
  protected virtual void Undo() { Console.WriteLine ("TextBox.Undo"); }
}

public class RichTextBox : TextBox
{
  protected override void Undo() { Console.WriteLine ("RichTextBox.Undo"); }
}

Flags enums:

[Flags]
public enum BorderSides { Left=1, Right=2, Top=4, Bottom=8 }
BorderSides leftRight = BorderSides.Left | BorderSides.Right;

if ((leftRight & BorderSides.Left) != 0)
   System.Console.WriteLine ("Includes Left");   // Includes Left

string formatted = leftRight.ToString();   // "Left, Right"

BorderSides s = BorderSides.Left;
s |= BorderSides.Right;
Console.WriteLine (s == leftRight);   // True

s ^= BorderSides.Right;               // Toggles BorderSides.Right
Console.WriteLine (s);                // Left  

Enum type safety issues:

static bool IsFlagDefined (Enum e)
{
  decimal d;
  return ! decimal.TryParse(e.ToString(), out d);
}

[Flags]
public enum BorderSides { Left=1, Right=2, Top=4, Bottom=8 }

static void Main()
{
  for (int i = 0; i <= 16; i++)
  {
    BorderSides side = (BorderSides)i;
    Console.WriteLine (IsFlagDefined (side) + " " + side);
  }
}

Generic types:

public class Stack<T>
{
  int position;
  T[] data = new T[100];
  public void Push (T obj)        { data[position++] = obj;  }
  public T Pop ()                 { return data[--position]; }
}
Stack<int> stack = new Stack<int>(); 
stack.Push(5);
stack.Push(10);
int x = stack.Pop();

Generic methods:

static void Swap<T> (ref T a, ref T b)
{
  T temp = b;
  a = b;
  b = temp;
}

default generic value:

static void Zap<T> (T[] array)
{
  for (int i = 0; i < array.Length; i++)
    array[i] = default(T);
}

Constraints:

static T Max <T> (T a, T b) where T : IComparable<T>
{
  return a.CompareTo (b) > 0 ? a : b;
}
static void Initialize<T> (T[] array) where T : new()
{
  for (int i = 0; i < array.Length; i++)
    array[i] = new T(); 
}
class Stack<T>
{
  Stack<U> FilteredStack<U>() where U : T {...}
}

Generics and covariance:

class Animal {}
class Bear : Animal {}
public class ZooCleaner
{
  public static void Wash<T> (Stack<T> animals) where T : Animal {}
}
Stack<Bear> bears = new Stack<Bear>();
ZooCleaner.Wash (bears);

Self-referencing generic declarations:

interface IEquatable<T> { bool Equals (T obj); }

public class Balloon : IEquatable<Balloon>
{
  string color;
  int cc;

  public bool Equals (Balloon b)
  {
    if (b == null) return false;
    return b.color == color && b.cc == cc;
  }
}

Uniqueness of static data in generic types:

public class Bob<T> { public static int Count; }

class Test
{
  static void Main()
  {
    Console.WriteLine (++Bob<int>.Count);     // 1
    Console.WriteLine (++Bob<int>.Count);     // 2
    Console.WriteLine (++Bob<string>.Count);  // 1
    Console.WriteLine (++Bob<object>.Count);  // 1
  }
}

Object initializers:

List<int> list = new List<int> {1, 2, 3};

 

© 2007, O'Reilly Media, Inc. All rights reserved

C# 3.0 in a Nutshell
Buy from amazon.com Available now