Exercises in this lecture   Go to the notes, in which this exercise belongs -- Keyboard shortcut: 'u'   Alphabetic index   Course home   

Exercise solution:
Static and dynamic types


The cause of confusion is that the method Report (implicitly) uses the virtual method ToString to print out the accounts. Therefore the dynamic types - and not the static types - are reported.

We can add a non-virtual method, StaticTypeString, to each of the bank account classes in order to get access to the static type of expressions of the form bai as BanktypeAccount.

Here is the modified program

using System; 

class App {

  public static void Main(){
  
    BankAccount ba1 = new BankAccount("George", 1000.0M, 0.01),
                ba2 = new CheckAccount("Bill", 2000.0M, 0.01);

    CheckAccount ca = new CheckAccount("John", 2000.0M, 0.01);

    Report((ba1 as BankAccount).StaticTypeString);
                                   
//    Report((ba1 as CheckAccount).StaticTypeString);    // null

    Report((ba2 as BankAccount).StaticTypeString);
                                   
    Report((ba2 as CheckAccount).StaticTypeString);

    Report((ca as BankAccount).StaticTypeString);

    Report((ca as CheckAccount).StaticTypeString);
  }

  public static void Report(string str){   // Report is, in reality, just Console.WriteLine(str)
    if (str != null)
      Console.WriteLine("{0}", str);
    else 
      Console.WriteLine("null");
  }

}


Here are the modified bank account classes. Notice in particular the non-virtual methods StaticTypeString, marked with new throughout the BankAccount class hierarchy:

using System;

public class BankAccount {

   protected double interestRate;
   protected string owner;
   protected decimal balance;

   public BankAccount(string o, decimal b, double ir) {
      this.interestRate = ir;
      this.owner = o; 
      this.balance = b;
   }

   public BankAccount(string o, double ir):
     this(o, 0.0M, ir) {
   }

   public virtual decimal Balance {
     get {return balance;}
   }

   public virtual void Withdraw (decimal amount) {
      balance -= amount;
   }

   public virtual void Deposit (decimal amount) {
      balance += amount;
   }

   public virtual void AddInterests() {
      balance += balance * (Decimal)interestRate;
   }    

   public override string ToString() {
      return owner + "'s account holds " +
            + balance + " kroner";
   }

   public string StaticTypeString {
    get{
      return "BankAccount";
    }
   }
} 

public class CheckAccount: BankAccount {

   public CheckAccount(string o, double ir): 
     base(o, 0.0M, ir) {
   }

   public CheckAccount(string o, decimal b, double ir): 
     base(o, b, ir) {
   }

   public override void Withdraw (decimal amount) {
      balance -= amount;
      if (amount < balance)
         interestRate = -0.10;
   }

   public override string ToString() {
      return owner + "'s check account holds " +
            + balance + " kroner";
   }

   public new string StaticTypeString {
    get{
      return "CheckAccount";
    }
   }
} 

public class LotteryAccount: BankAccount {

   private static Lottery lottery  = Lottery.Instance(20);

   public LotteryAccount(string o, decimal b): 
     base(o, b, 0.0) {
   }

   public override void AddInterests() {
      int luckyNumber = lottery.DrawLotteryNumber;
      balance = balance + lottery.AmountWon(luckyNumber);
   }    

   public override string ToString() {
      return owner + "'s lottery account holds " +
            + balance + " kroner";
   }

   public new string StaticTypeString {
     get{
      return "LotteryAccount";
     }
   }
} 

public class SavingsAccount: BankAccount {

   public SavingsAccount(string o, double ir): 
     base(o, 0.0M, ir) {
   }

   public SavingsAccount(string o, decimal b, double ir): 
     base(o, b, ir) {
   }

   public override void Withdraw (decimal amount) {
      if (amount < balance)
          balance -= amount;
      else
          throw new Exception("Cannot withdraw");
   }

   public override void AddInterests() {
      balance = balance + balance * (decimal)interestRate 
                        - 100.0M;
   }    

   public override string ToString() {
      return owner + "'s check account holds " +
            + balance + " kroner";
   }

   public new string StaticTypeString {
    get{
      return "SavingsAccount";
    }
   }
} 

public class Lottery{

  private static Random rdm = new Random(unchecked((int)DateTime.Now.Ticks));

  private int difficulty;
  private readonly int winningNumber;
  private readonly decimal amountWon;
  private static Lottery uniqueInstance = null;

  private Lottery(int difficulty){
    this.difficulty = difficulty;
    this.winningNumber = rdm.Next(difficulty);
    this.amountWon = 500000.00M;
  }

  public static Lottery Instance(int difficulty){
    if (uniqueInstance == null)
      uniqueInstance = new Lottery(difficulty);
    return uniqueInstance;
  }

  public int DrawLotteryNumber{
    get {return rdm.Next(difficulty);}
  }

  public decimal AmountWon(int luckyNumber){
    decimal res;
    if (luckyNumber == winningNumber)
       res = amountWon;
    else
       res = 0.0M;
    return res;
  }
}