Chapter 4
Reference types, Value types, and Patterns

Kurt Nørmark ©
Department of Computer Science, Aalborg University, Denmark


Abstract
Previous lecture Next lecture
Index References Contents
In this lecture we continue our basic coverage fundamental topics in object-oriented programming. First we contrast objects and values, as defined by classes and structs. Next, we study organizations of C# programs. In the final part we introduce the ideas of object-oriented design patterns.


Reference Types

Reference Types
Slide Annotated slide Contents Index
References Textbook 
Classes are reference types in C#. It is therefore important to understand this concept.

A class is a reference type

Objects instantiated from classes are accessed by references

The objects are allocated on the heap

Instances of classes are dealt with by use of so-called reference semantics

  • Reference semantics:

    • Assignment, parameter passing, and return manipulates references to objects

  • The heap:

    • The memory area where instances of classes are allocated

    • Allocation takes place when a class is instantiated

    • Deallocation takes place when the object no longer affects the program

      • In practice, when there are no references left to the object

      • By a separate thread of control called the garbage collector

Reference

Illustration of variables of reference types
Slide Annotated slide Contents Index
References Textbook 
On this page we explain what happens in the assignment p1 = p2, where both p1 and p2 are variables of compatible reference types. See the similar example for value types on a later page.

Variables of reference types. The situation before the assignment p1 = p2.

Program: The variables p1 and p2 refer to two points. p1 is assigned to p2.
Point p1 = new Point(1.0, 2.0),
      p2 = new Point(3.0, 4.0);

p1 = p2;

Variables of reference types. The situation after the assignment p1 = p2.

Reference

Overview of reference types in C#
Slide Annotated slide Contents Index
References Textbook 
There are other reference types than classes. This page provides an overview of the reference types in C#.

Classes are reference types in C#, but there are others as well

  • Reference types

    • Classes

      • Strings

      • Arrays

    • Interfaces

      • Similar to classes. Contain no data. Have only signatures of methods

    • Delegates

      • Delegate objects can contain one or more methods

      • Used when we deal with methods as data

References

Comparing and copying objects via references
Slide Annotated slide Contents Index
References Textbook 
This page introduces three types of equality: reference equality, shallow equality, and deep equality. A similar categorization is used for copying.

Do we compare references or the referenced objects?

Do we copy the reference or the referenced object?

How deep do we copy objects that reference other objects?

  • Comparing

    • Reference comparison

      • Compares if two references refers to objects created by the same execution of new

    • Shallow and deep comparison

      • Pair-wise comparison of fields - at varying levels

  • Copying:

    • Reference copying

    • Shallow or deep copying

      • Is also known as cloning

      • Somehow supported by the method MemberwiseClone in System.Object

Reference

An assignment of the form var = obj1 copies a reference

A comparison of the form obj1 == obj2 compares references (unless overloaded)

Equality in C#
Slide Annotated slide Contents Index
References Textbook 
There are several different equality operations in C#. This page provides an overview.

There are several equality operations in C# on reference types

References

  • o1.Equals(o2)     - equality

    • By default, true if o1 and o2 are created by execution of the same new

    • Can be redefined in a particular class

  • Object.ReferenceEquals(o1, o2)     - identity

    • True if both o1 and o2 are null, or if they are created by execution of the same new

    • Static - cannot be redefined.

  • Object.Equals(o1, o2)

    • True if Object.ReferenceEquals(o1, o2), or if o1.Equals(o2)

  • o1 == o2

    • True if both o1 and o2 are null, or if they are created by execution of the same new

    • An overloadable operator

Exercise 4.2. Equality of value types and reference types

Take a close look at the following program which uses the == operator on integers.

using System;

class WorderingAboutEquality{

  public static void Main(){
    int i = 5,
        j = 5;

    object m = 5,
           n = 5;  

    Console.WriteLine(i == j);
    Console.WriteLine(m == n);
  }

}

Predict the result of the program. Compile and run the program, and explain the results. Were your predictions correct?


Value Types

Value types
Slide Annotated slide Contents Index
References Textbook 
Value types stand as a contrast to reference types. There are dramatic difference between the use of value types and reference types in C#.

A variable of value type contains its value

The values are allocated on the method stack or within objects on the heap

Variables of value types are dealt with by use of so-called value semantics

Use of value types simplifies the management of short-lived data

  • Value semantics

    • Assignment, call-by-value parameter passing, and return copy entire values

  • The method stack

    • The memory area where short-lived data is allocated

      • Parameters and local variables

    • Allocation takes place when an operation, such as a method, is called

    • Deallocation takes place when the operation returns

Reference

Illustration of variables of value types
Slide Annotated slide Contents Index
References Textbook 
This page illustrates assignment on value types, in the same way as done for reference types on an earlier page.

Assume that the type Point is a value type

Variables of value types. The situation before the assignment p1 = p2.

Program: The variables p1 and p2 refer to two points. p1 is assigned to p2.
Point p1 = new Point(1.0, 2.0),
      p2 = new Point(3.0, 4.0);

p1 = p2;

Variables of value types. The situation after the assignment p1 = p2.

Reference

Structs in C#
Slide Annotated slide Contents Index
References Textbook 
Structs in C# are value types. On this page we show two concrete programming examples of structs.

We show the structs Point and Card

Program: Struct Point.
using System;

public struct Point {
  private double x, y;

  public Point(double x, double y){
   this.x = x; this.y = y;
  }

  public double Getx (){
    return x;
  }

  public double Gety (){
    return y;
  }

  public void Move(double dx, double dy){
    x += dx; y += dy;
  }

  public override string ToString(){
    return "Point: " + "(" + x + "," + y + ")" + ".";
  }
}

Program: Struct Card.
using System;

public enum CardSuite:byte 
          {Spades, Hearts, Clubs, Diamonds };
public enum CardValue: byte 
          {Ace = 1, Two = 2, Three = 3, Four = 4, Five = 5, 
           Six = 6, Seven = 7, Eight = 8, Nine = 9, Ten = 10, 
           Jack = 11, Queen = 12, King = 13};

public struct Card {
  private CardSuite suite;
  private CardValue value;

  public Card(CardSuite suite, CardValue value){
   this.suite = suite;
   this.value = value;
  }
 
  public Card(CardSuite suite, int value){
    this.suite = suite;
    this.value = (CardValue)value;
  }
 
  public CardSuite Suite(){
    return this.suite;
  }
 
  public CardValue Value (){
    return this.value;
  }
 
  public System.Drawing.Color Color (){
   System.Drawing.Color result;
   if (suite == CardSuite.Spades || suite == CardSuite.Clubs)
     result = System.Drawing.Color.Black;
   else
     result = System.Drawing.Color.Red;
   return result;
  }
 
  public override String ToString(){
    return String.Format("Suite:{0}, Value:{1}, Color:{2}", 
                         suite, value, Color().ToString());
  }
}

Program: A client of struct Card.
using System;

public class PlayingCardClient{

  public static void Main(){
    Card c1 = new Card(CardSuite.Spades, CardValue.King),
         c2 = new Card(CardSuite.Hearts, 1),
         c3 = new Card(CardSuite.Diamonds, 13),
         c4;

    c4 = c1;  // Copies c1 into c4

    Console.WriteLine(c1);
    Console.WriteLine(c2);
    Console.WriteLine(c3);
    Console.WriteLine(c4);
  }

}

Structs are typically used for aggregation and encapsulation of a few values, which we want to treat as a value itself, and for which we wish to apply value semantics

In the System namespace, the types DateTime and TimeSpan are programmed as structs

References

Structs and Initialization
Slide Annotated slide Contents Index
References Textbook 
Structs have constructors in the same way as classes. There are, however, a few special rules of constructors in structs. These special rules are described on this page.

The means for initialization of struct values are slightly different from the means for initialization of class instances (objects)

Program: Fields in structs cannot have initializers.
/* Right, Wrong */
using System;

// Error: 
// Cannot have instance field initializers in structs.
public struct StructOne{
  int a = 5;
  double b = 6.6;
}

// OK:
// Fields in structs are initialized to default values.
public struct StructTwo{
  int a;
  double b;
}

Program: An explicit parameterless constructor is not allowed.
/* Right, Wrong */
using System; 

// Error: 
// Structs cannot contain explicit parameterless constructors.
public struct StructThree{
  int a;
  double b;

  public StructThree(){
    a = 1;
    b = 2.2;
  }
}

// OK:
// We can program a constructor with parameters.
// The implicit parameterless constructor is still available.
public struct StructFour{
  int a;
  double b;

  public StructFour(int a, double b){
    this.a = a;
    this.b = b;
  }
}

Reference

Initializers cannot be used in Structs

The parameterless default constructor cannot be redefined.

Structs versus classes
Slide Annotated slide Contents Index
References Textbook 
Here we present a back-to-back comparison of classes and structs.

Structs and classes are similar in C#

Classes

Structs

Reference type

Value type

Used with dynamic instantiation

Used with static instantiation

Ancestors of class Object

Ancestors of class Object

Can be extended by inheritance

Cannot be extended by inheritance

Can implement one or more interfaces

Can implement one or more interfaces

Can initialize fields with initializers

Cannot initialize fields with initializers

Can have a parameterless constructor

Cannot have a parameterless constructor

Reference

Examples of mutable structs in C#
Slide Annotated slide Contents Index
References Textbook 
The purpose of this page is to point out the misfit between value types (C# structs) and mutability. This page should be used as motivation for the solutions on the following page.

Examples of struct Point

The examples on this page illustrate mutable Points programmed with structs

Reference

Program: The struct Point - mutable.
using System;

public struct Point {
  private double x, y;

  public Point(double x, double y){
   this.x = x; this.y = y;
  }

  public double Getx (){
    return x;
  }

  public double Gety (){
    return y;
  }

  public void Move(double dx, double dy){
    x += dx; y += dy;
  }

  public override string ToString(){
    return "Point: " + "(" + x + "," + y + ")" + ".";
  }
}

Program: Moving a point by mutation.
using System;

public class Application{

  public static void Main(){
    Point p1 = new Point(1.0, 2.0);

    p1.Move(3.0, 4.0);     // p1 has moved to (4.0, 6.0)
    p1.Move(5.0, 6.0);     // p1 has moved to (9.0, 12.0)

    Console.WriteLine("{0}", p1);
  }

}

Program: Output from the application.
Point: (9,12).

Program: The struct Point - mutable, where move returns a Point.
using System;

public struct Point {
  private double x, y;

  public Point(double x, double y){
    this.x = x; this.y = y;
  }

  public double Getx (){
    return x;
  }

  public double Gety (){
    return y;
  }

  public  Point  Move(double dx, double dy){
    x += dx; y += dy; 
    return this;  // returns a copy of the current object
  }

  public override string ToString(){
    return "Point: " + "(" + x + "," + y + ")" + ".";
  }
}

Program: Application the struct Point - Cascaded moving.
using System;

public class Application{

   public static void Main(){
    Point p1 = new Point(1.0, 2.0);
    
    p1.Move(3.0, 4.0).Move(5.0, 6.0);
    Console.WriteLine("{0}", p1);      // Where is p1 located?
  }
}

Program: Output from the application.
Point: (4,6).

Examples of immutable structs in C#
Slide Annotated slide Contents Index
References Textbook 
Immutable structs fits nicely with value semantics. This is illustrated with the continuation of the example from the previous page.

An example of immutable points in C# and functional programming style

Program: The struct Point - immutable.
using System;

public struct Point {
  private readonly double x, y;

  public Point(double x, double y){
    this.x = x; this.y = y;
  }

  public double Getx (){
    return x;
  }

  public double Gety (){
    return y;
  }

  public Point Move(double dx, double dy){
    return new Point(x+dx, y+dy);
  }

  public override string ToString(){
    return "Point: " + "(" + x + "," + y + ")" + ".";
  }
}

Program: Application the struct Point - immutable.
using System;

public class Application{

   public static void Main(){
     Point p1 = new Point(1.0, 2.0),
           p2;
    
     p2 = p1.Move(3.0, 4.0).Move(5.0, 6.0);
     Console.WriteLine("{0} {1}", p1, p2);
   }

}

Program: Output from the application.
Point: (1,2). Point: (9,12).

There is a misfit between mutable datatypes and use of value semantics

It is recommended to use structs in C# together with a functional programming style

Exercise 4.3. Are playing cards and dice immutable?

Evaluate and discuss the classes Die and Card with respect to mutability.

Make sure that you understand what mutability means relative to the concrete code. Explain it to your fellow programmers!

More specific, can you argue for or against the claim that a Die instance/value should be mutable?

And similarly, can you argue for or against the claim that a Card instance/value should be mutable?

Why is it natural to use structs for immutable objects and classes for mutable objects? Please compare your findings with the remarks in 'the solution' when it is released.

Boxing and Unboxing
Slide Annotated slide Contents Index
References Textbook 
Boxing is a bridge from value types to reference types.

The use of boxing facilitates compatibility between value types and reference types

The concept boxing: Boxing involves a wrapping of a value in an object of the class Object
The concept unboxing: Unboxing involves an unwrapping of a value from an object

  • Boxing

    • Done as an implicit type conversion

    • Involves allocation of a new object on the heap and copying of the value into that object

  • Unboxing

    • Done explicitly via a type cast

    • Involves extraction of the value of a box

Program: A program that illustrates boxing and unboxing.
using System;

public class BoxingTest{
  public static void Main(){
    int i = 15, j, k;
  
    bool b = false, c, d;
    Object obj1 = i,   // boxing of the value of i
           obj2 = b;   // boxing of the value of b
  
    j = (int) obj1;    // unboxing obj1
    c = (bool) obj2;   // unboxing obj2

//  k = i + obj1;    // Compilation error
//  d = b && obj2;   // Compilation error

    k = i + (int)obj1;    
    d = b && (bool)obj2;  
  
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}",
                      i, obj1, b, obj2, j, c, k, d);

  }
}

Program: Output of the boxing and unboxing program.
15, 15, False, False, 15, False, 30, False

Nullable types
Slide Annotated slide Contents Index
References Textbook 
Nullable types fuse an extra null value into existing value types.

A variable of reference type can be null

A variable of value type cannot be null

A variable of nullable value type can be null

Program: An integer sequence with Min and Max operations - a naive attempt.
public class IntSequence {

  private int[] sequence;

  public IntSequence(params int[] elements){
    sequence = new int[elements.Length];
    for(int i = 0; i < elements.Length; i++){
      sequence[i] = elements[i];
    }
  }

  public int Min(){
    int theMinimum;
    if (sequence.Length == 0)
      return -1;
    else {
      theMinimum = sequence[0];
      foreach(int e in sequence)    
        if (e < theMinimum)
           theMinimum = e;
    }
    return theMinimum;
  }

  public int Max(){
    int theMaximum;
    if (sequence.Length == 0)
      return -1;
    else {
      theMaximum = sequence[0];
      foreach(int e in sequence)    
        if (e > theMaximum)
           theMaximum = e;
    }
    return theMaximum;
  }

  // Other useful sequence methods

}

Program: An integer sequence with Min and Max operations - with int?.
public class IntSequence {

  private int[] sequence;

  public IntSequence(params int[] elements){
    sequence = new int[elements.Length];
    for(int i = 0; i < elements.Length; i++){
      sequence[i] = elements[i];
    }
  }

  public int? Min(){
    int theMinimum;
    if (sequence.Length == 0)
      return null;
    else {
      theMinimum = sequence[0];
      foreach(int e in sequence)    
        if (e < theMinimum)
           theMinimum = e;
    }
    return theMinimum;
  }

  public int? Max(){
    int theMaximum;
    if (sequence.Length == 0)
      return null;
    else {
      theMaximum = sequence[0];
      foreach(int e in sequence)    
        if (e > theMaximum)
           theMaximum = e;
    }
    return theMaximum;
  }

  // Other useful sequence methods

}

Program: A client of IntSequence.
using System;

class IntSequenceClient{

  public static void Main(){
    IntSequence is1 = new IntSequence(-5, -1, 7, -8, 13),
                is2 = new IntSequence();

    ReportMinMax(is1);
    ReportMinMax(is2);     
  }

  public static void ReportMinMax(IntSequence iseq){
    if (iseq.Min().HasValue && iseq.Max().HasValue)
      Console.WriteLine("Min: {0}. Max: {1}", 
                         iseq.Min(), iseq.Max());
    else
      Console.WriteLine("Int sequence is empty");
  }

}

Program: Program output.
Min: -8. Max: 13
Int sequence is empty

  • Nullable types in C#:

    • Many operators are lifted such that they also are applicable on nullable types

    • An implicit conversion can take place from t to t?

    • An explicit conversion (a cast) is needed from t? to t


Organization of C# Programs

Program Organization
Slide Annotated slide Contents Index
References Textbook 

The structure and organization of a C# program is radically different from the structure and organization of both C programs and Java programs.

C# programs declare types which contain members. Types are organized in namespaces.

  • C# programs are organized in namespaces

    • Namespace can contain types and - recursively - other namespaces

  • Namespaces (and classes) in relation to files:

    • One of more namespaces in a single file

    • A single namespace in several files

    • A single class in several files - partial classes

  • The mutual order of declarations is not significant in C#.

    • No declaration before use.

  • When compiled, C# programs are organized in assemblies

    • .exe and .dll files

Examples of Program Organization
Slide Annotated slide Contents Index
References Textbook 

Program: A single class in the anonymous default namespace.
// The most simple organization
// A class located directly in the global namespace
// In source file ex.cs

using System;

public class C { 

  public static void Main(){
    Console.WriteLine("The start of the program");
  }

}

Program: Compilation of a single class in the anonymous default namespace.
In Windows SDK 
csc ex.cs

In Mono
gmcs ex.cs

Program: Two namespaces and a nested namespace with classes.
// Several namespaces, including nested namespaces.
// In source file ex.cs

namespace N1 {
  public class C1{};
}


namespace N2 {
  internal class C2{};
  public class C3{};

  namespace N3 {
     public class C4{
       C2 v;
     }
  }
}

Program: A client of classes in different namespaces.
// A client program
// In source file client.cs


/*
  Namespace N1
    public class C1
  Namespace N2
    internal class C2 
    public class C3 
    Namespace N3
      public class C4
*/

using N1;
using N2;
using N2.N3;

public class Client{
  C1 v = new C1(); 

  // The type or namespace name 'C2' could not be found.
  // C2 w = new C2();   
  C3 x = new C3(); 
  C4 y = new C4(); 
}

Program: An equivalent client of classes in different namespaces - no using clauses.
// An equivalent client program. No using clauses.
// In source file client-equiv.cs


/*
  Namespace N1
    public class C1
  Namespace N2
    internal class C2 
    public class C3 
    Namespace N3
      public class C4
*/


// No using clauses - instead fully qualified class names:

public class Client{
  N1.C1 v = new N1.C1(); 

  // The type or namespace name 'C2' could not be found.
  // N2.C2 w = new N2.C2();   
  N2.C3 x = new N2.C3(); 
  N2.N3.C4 y = new N2.N3.C4(); 
}

Program: Compilation of the namespaces and client.
In Windows SDK
csc /target:library ex.cs
csc /target:library /reference:ex.dll client.cs
csc /target:library /reference:ex.dll client-equiv.cs

In MONO
gmcs /target:library ex.cs
gmcs /target:library /reference:ex.dll client.cs
gmcs /target:library /reference:ex.dll client-equiv.cs

Program: Nested namespaces with classes.
// Again nested namespaces. N1 contains N2 and N3.
// In source file ex.cs

namespace N1 {

  namespace N2 {
   public class C1{};
   public class C2{};
  }

  namespace N3 {
      public class C3{}
  }

}

Program: Equivalent program with nested namespaces - no physical nesting.
// Equivalent to the previous program 
// No physical namespace nesting 
// In source file ex-equiv.cs

namespace N1.N2 {
  public class C1{};
  public class C2{};
}

namespace N1.N3 {
  public class C3{}
}

Program: A client of classes in nested namespaces.
// In source file client.cs

using N1.N2;
using N1.N3;

public class Client{
  C1 x = new C1(); 
  C2 y = new C2(); 
  C3 z = new C3(); 
}

Program: Compilation of the namespaces and client.
In Windows SDK
csc /target:library ex.cs
csc /target:library /reference:ex.dll client.cs

csc /t:library ex-equiv.cs
csc /t:library /r:ex-equiv.dll /out:client-equiv.dll client.cs

Program: Part one of namespace Intro with the classes A and B.
// f1.cs: First part of the namespace Intro

using System;

namespace Intro{

  internal class A {

    public void MethA (){
      Console.WriteLine("This is MethA in class Intro.A");
    }
  }

  public class B {

    private A var = new A();

    public void MethB (){
      Console.WriteLine("This is MethB in class Intro.B");
      Console.WriteLine("{0}", var);
    }
  }

}

Program: Part two of namespace Intro with the class C.
// f2.cs: Second part of the namespace Intro

using System;

namespace Intro{

  public class C {
    private A var1 = new A();   
    private B var = new B();

    public void MethC (){
      Console.WriteLine("This is MethC in class Intro.C");
      Console.WriteLine("{0}", var);
      Console.WriteLine("{0}", var1);
    }
  }
}

Program: A client class that uses the classes in namespace Intro.
// client.cs: A client of the classes in namespace Intro


/* 
    namespace Intro
      internal class A
      public class B
      public class C
*/


using System;
using Intro;

class Client {

  public static void Main(){
//  A a = new A();   // Compiler error: Class A is internal in Intro
    B b = new B();
    C c = new C();

    Console.WriteLine("{0} {1}", b, c);
  }
}

Program: Possible compilation of namespace Intro and class Client.
In Windows SDK
csc /target:library /out:assembly.dll f1.cs f2.cs
csc /reference:assembly.dll client.cs

client.exe


In MONO:
gmcs /target:library /out:assembly.dll f1.cs f2.cs
gmcs /reference:assembly.dll client.cs

mono client.exe

Namespaces and Visibility
Slide Annotated slide Contents Index
References Textbook 

Which kinds of visibility apply for types and namespaces in namespaces?

  • Types declared in a namespace

    • Can either have public or internal access

    • The default visibility is internal

    • Internal visibility is relative to an assembly - not a namespace

  • Namespaces in namespaces

    • There is no visibility attached to namespaces

    • A namespace is implicitly public within its containing namespace

References

Namespaces and Assemblies
Slide Annotated slide Contents Index
References Textbook 

  • Namespaces

    • The top-level construct in a compilation unit

    • May contain types (such as classes) and nested namespaces

    • Identically named members of different namespaces can co-exist

    • There is no coupling between classes/namespaces and source files/directories

  • Assemblies

    • A packaging construct produced by the compiler

      • Not a syntactic construct in C#

    • A collection of compiled types - together with resources on which the types depend

    • Versioning and security settings apply to assemblies

The file/directory organization, the namespace/class organization and the assembly organization are relatively independent of each other


Patterns and Techniques

Design Patterns
Slide Annotated slide Contents Index
References Textbook 

The concept design pattern: A pattern is a named nugget of instructive information that captures the essential structure and insight of a successful family of proven solutions to a recurring problem that arises within a certain context and system of forces [Brad Appleton]

  • Worth noticing

    • Named: Eases communication about problems and solutions

    • Nugget: Emphasizes the value of the pattern

    • Recurring problem: A pattern is intended to solve a problem that tends to reappear.

    • Proven solution: The solution must be proven in a number of existing programs

    • Nontrivial solution: We are not interested in collecting trivial and obvious solutions

    • Context: The necessary conditions and situation for applying the pattern

    • Forces: Considerations and circumstances, often in mutual conflict with each other

Reference

Object-oriented Design Patterns
Slide Annotated slide Contents Index
References Textbook 

Object-oriented design patterns were introduced in the book "Design Patterns - Elements of Reusable Object-Oriented Software" by Gamma, Helm, Johnson and Vlissides.

  • Twenty three patterns categorized as

    • Creational Patterns

      • Abstract factory, Factory method, Singleton, ...

    • Structural Patterns

      • Adapter, Composite, Proxy, ...

    • Behavioral Patterns

      • Command, Iterator, Observer, ...

Reference

There are patterns in a variety of different areas, and at various levels of abstractions

The Singleton pattern
Slide Annotated slide Contents Index
References Textbook 

Problem: For some classes we wish to guarantee that at most one instance of the class can be made.

Solution: The singleton design pattern

Program: A template of a singleton class.
public class Singleton{

  // Instance variables

  private static Singleton uniqueInstance = null;

  private Singleton(){
     // Initialization of instance variables
  }

  public static Singleton Instance(){
    if (uniqueInstance == null)
      uniqueInstance = new Singleton();

    return uniqueInstance;
  }

  // Methods

}

Program: A singleton Die class.
using System;

public class Die {
  private int numberOfEyes;
  private Random randomNumberSupplier; 
  private int maxNumberOfEyes;

  private static Die uniqueInstance = null;

  private Die (){
    randomNumberSupplier = new Random(unchecked((int)DateTime.Now.Ticks));
    this.maxNumberOfEyes = 6;
    this.Toss();
  }

  public static Die Instance(){
    if (uniqueInstance == null)
      uniqueInstance = new Die();

    return uniqueInstance;
  } 
    
  public void Toss (){
    numberOfEyes = randomNumberSupplier.Next(1,maxNumberOfEyes + 1);
  }

  public int NumberOfEyes() {
    return numberOfEyes;
  }

  public override String ToString(){
    return String.Format("Die[{0}]: {1}", maxNumberOfEyes, numberOfEyes);
  }
}

Program: Application of the singleton Die class.
using System;

class diceApp {

  public static void Main(){

//  Die d1 = new Die();   //  Compile-time error:
                          //  The type 'Die' has no constructors defined

    Die d2 = Die.Instance(),
        d3 = Die.Instance();

    for(int i = 1; i < 5; i++){
      Console.WriteLine(d2); 
      d2.Toss(); 
    }

    for(int i = 5; i < 10; i++){
      Console.WriteLine(d2); 
      d3.Toss(); 
    }

    // Test for singleton:
    if (d2 == d3)
      Console.WriteLine("d2 and d3 refer to same die instance");
    else
      Console.WriteLine("d2 and d3 do NOT refer to same die instance");
 }

}

Program: Output of singleton Die application.
Die[6]: 2
Die[6]: 3
Die[6]: 5
Die[6]: 4
Die[6]: 5
Die[6]: 6
Die[6]: 1
Die[6]: 4
Die[6]: 4
d2 and d3 refer to same die instance

A singleton Die is - most probably - not very useful.

On the next slide we will show a singleton Random, which solves a real problem

A Singleton Random Class
Slide Annotated slide Contents Index
References 

Earlier in this lecture we had a problem with one Random object per Die object

References

Program: A singleton Random class.
using System;

public class Random {

  // Singleton pattern:
  // Keeps track of unique instance of this class
  private static Random uniqueInstance = null;

  // Holds the instance of System.Random
  private System.Random systemRandom;

  // Singleton pattern: Private constructor.
  private Random(){
    systemRandom = new System.Random(unchecked((int)DateTime.Now.Ticks));
  }

  public static Random Instance(){
    if (uniqueInstance == null)
      uniqueInstance = new Random();
    return uniqueInstance;
  }

  public int Next(int lower, int upper){
    // delegate to systemRandom
    return systemRandom.Next(lower,upper);
  }

}

Program: A Die class that uses singleton Random.
using System;

public class Die {
  private int numberOfEyes;
  private Random randomNumberSupplier; 
  private const int maxNumberOfEyes = 6;

  public Die(){
    randomNumberSupplier = Random.Instance();
    numberOfEyes = NewTossHowManyEyes();
  }   
    
  public void Toss(){
    numberOfEyes = NewTossHowManyEyes();
  }

  private int NewTossHowManyEyes (){
    return randomNumberSupplier.Next(1,maxNumberOfEyes + 1);
  }

  public int NumberOfEyes() {
    return numberOfEyes;
  }

  public override String ToString(){
    return String.Format("[{0}]", numberOfEyes);
  }
}

Program: A Die application class.
using System;

class diceApp {

  public static void Main(){

    Die d1 = new Die(),
        d2 = new Die();

    for(int i = 1; i < 10; i++){
      Console.WriteLine("Die 1: {0}", d1);  
      Console.WriteLine("Die 2: {0}", d2);  
      d1.Toss();  d2.Toss(); 
    }

 }
}

Program: Output from the Die application class.
Die 1: [6]
Die 2: [1]
Die 1: [5]
Die 2: [2]
Die 1: [3]
Die 2: [2]
Die 1: [3]
Die 2: [2]
Die 1: [6]
Die 2: [1]
Die 1: [4]
Die 2: [2]
Die 1: [6]
Die 2: [5]
Die 1: [1]
Die 2: [2]
Die 1: [1]
Die 2: [1]

By using a Singleton Random we ensure that there exists only one Random object

Reference

Factory methods
Slide Annotated slide Contents Index
References Textbook 

Problem: If many instances of the expression new C(...) are sprinkled across a program we have very strong bindings to creation of C - as opposed to an alternative class D

It is sometimes awkward to program adequate constructors because we cannot chose good name for them, and we cannot always control the parameters of constructors

Solution: Create objects via one or more factory methods - typically class methods

There exist related object-oriented design patterns called Factory Method and Abstract Factory, both of which involve inheritance

Reference

Examples of Static Factory Methods
Slide Annotated slide Contents Index
References Textbook 

In class Point it may be relevant to construct both polar and rectangular points

Program: A clumsy attempt with two overloaded constructors.
using System;

public class Point {

  public enum PointRepresentation {Polar, Rectangular}
  private double r, a;    // polar data repr: radius, angle

  // Construct a point with polar coordinates
  public Point(double r, double a){
     this.r = r;
     this.a = a;
  }  

  // Construct a point, the representation of which depends
  // on the third parameter.
  public Point(double par1, double par2, PointRepresentation pr){
   if (pr == PointRepresentation.Polar){
     r = par1; a = par2;
   } 
   else {
     r = RadiusGivenXy(par1,par2);
     a = AngleGivenXy(par1,par2);
   }
  }  

  private static double RadiusGivenXy(double x, double y){
    return Math.Sqrt(x * x + y * y);
  }

  private static double AngleGivenXy(double x, double y){
    return Math.Atan2(y,x);
  }

  // Remaining Point operations not shown
}

Program: A better solution with static factory methods.
using System;

public class Point {

  public enum PointRepresentation {Polar, Rectangular}

  private double r, a;     // polar data repr: radius, angle

  // Construct a point with polar coordinates
  private Point(double r, double a){
     this.r = r;
     this.a = a;
  }

  public static Point MakePolarPoint(double r, double a){
    return new Point(r,a);
  }

  public static Point MakeRectangularPoint(double x, double y){
    return new Point(RadiusGivenXy(x,y),AngleGivenXy(x,y));
  }

  private static double RadiusGivenXy(double x, double y){
    return Math.Sqrt(x * x + y * y);
  }

  private static double AngleGivenXy(double x, double y){
    return Math.Atan2(y,x);
  }

  // Remaining Point operations not shown
}

In struct Interval it would be natural to have a parameterless constructor, but it is illegal

Program: A clumsy attempt with two overloaded constructors and an illegal constructor.
using System;
using System.Collections;

public struct Interval{

  private readonly int from, to;
  private readonly bool empty;

  // Construct a non-empty interval [from - to].
  public Interval(int from, int to){
    this.empty = false;
    this.from = from;
    this.to = to;
  }

  // Construct an empty interval. The parameter is a dummy.
  public Interval(int notUsed){
    this.empty = true;
    this.from = 0;  // not really used
    this.to = 0;    // not really used
  }

  // Illegal constructor. Compile-time error.
  // Structs cannot contain explicit parameterless constructors
  public Interval(){
    this.empty = true;
    this.from = 0; 
    this.to = 0;   
  }    

  // Other Interval operations not shown   
}

Program: A better solution with static factory methods.
using System;
using System.Collections;

public struct Interval{

  private readonly int from, to;
  private readonly bool empty;

  // Construct a non-empty interval [from - to] if empty is false.
  // Else construct an empty interval (from and to are not used). 
  private Interval(int from, int to, bool empty){
    this.empty = empty;
    this.from = from;
    this.to = to;
  }

  public static Interval MakeInterval(int from, int to){
    return new Interval(from,to,false);
  }

  public static Interval MakeEmptyInterval(){
    return new Interval(0,0,true);
  }

  // Other Interval operations not shown   
}

Chose a coding style in which factory methods are consistently named: Make...(...)

Privacy Leaks
Slide Annotated slide Contents Index
References Textbook 

Problem: A method can return part of its private state, which can be mutated outside the object

Program: A Mutable Date class.
// A mutable Date
public class Date{
  private ushort year;
  private byte month, day;

  public Date(ushort year, byte month, byte day){
    this.year = year; this.month = month; this.day = day;
  }

  public void SetYear(ushort year){  
    this.year = year;                
  }

  public ushort GetYear(){
    return year;
  }

  // Similar month and day setters and getter methods

  public override string ToString(){
    return string.Format("{0}.{1}.{2}",day, month, year);
  }
}

Program: A Person class that can return its private date of birth.
public class Person{

  private string name;
  private Date dateOfBirth, dateOfDeath;  

  public Person (string name, Date dateOfBirth){
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.dateOfDeath = null;
  }

  public Date GetDateOfBirth(){
     return dateOfBirth;                  
  }                                       

  public ushort AgeAsOf(Date d){
    return (ushort)(d.GetYear() - dateOfBirth.GetYear());
  }

  public bool Alive(){
    return dateOfDeath == null;
  }

  public override string ToString(){
    return "Person: " + name + " " + dateOfBirth;
  }

  // Other getter and setter methods

}

Program: A client of the Person which modifies the returned birth Date.
using System;

class Client{

  public static void Main(){

    Person p = new Person("Hanne", new Date(1926, 12, 24));  

    Date d = p.GetDateOfBirth();               
    d.SetYear((ushort)(d.GetYear() - 100));    
    Console.WriteLine("{0}", p);

    Date today = new Date(2006,9,27);
    Console.WriteLine("Age of Hanne as of {0}: {1}.", 
                      today, p.AgeAsOf(today));   
  }

}

Program: The output of the Person client program.
Person: Hanne 24.12.1826
Age of Hanne as of 27.9.2006: 180.

Exercise 4.6. Privacy Leaks

The starting point of this exercise is the observations about privacy leaks on the accompanying slide.

Make sure that you understand the problem. Test-drive the program (together with its dependent Person class and Date class) on your own computer.

If you have not already done so, read the section about privacy leaks in the textbook!

Find a good solution to the problem, program it, and test your solution.

Discuss to which degree you will expect that this problem to occur in everyday programming situations.

Exercise 4.6. Mutable and immutable Point objects with Move methods

On the slides about mutable and immutable structs we have illustrated two versions of struct Point with two different implementations of the Move method.

In the first struct, Move mutates the x and y coordinates and it returns this- the current object.

The second struct is immutable and Move returns a new instance of Point which is moved relative to the receiver of the Move message.

For both versions of Point we use the same client class, which sends Move messages to different points. In the lecture - and in the textbook - the differences between the first struct and the second struct has been explained relative to the client class. Be sure that you understand the difference!

Now, you are asked to implement version 1 and version 2 of Point from above with classes instead of structs. Compile and run each of them relative to the given client class. In each of the two cases explain the behavior of the Move methods in the classes.

In each of the two cases, explain how many instances of class Point there is created.

Exercise 4.6. Pyramid BankAccounts

This exercise can be seen as a continuation of the bank account exercise in which we supplied a bank account with a backup account. The primary purpose of the exercises is to train recursive object-oriented programming.

We organize bank accounts in a tree structure (called a pyramid) in which each bank account may have two sons which are also bank accounts. The money in the account are somehow distributed on the accounts of the pyramid.

Program a version of class BankAccount with an owner, a balance, and possible references to two additional bank accounts: leftAccount and rightAccount. A skeleton of this class is provided together with a client program. You can assume that an account is either a leaf or the account has two sons.

  • Program the Balance method. It should add together the contributions from all accounts in the pyramid.
  • Program the method Deposit. The message ba.Deposit(amount) inserts one third of the amount in the receiver, one third in the left part of the pyramid (recursively), and one third in the right part of the pyramid (recursively). In case of a leaf account, ordinary simple depositing is used.
  • Program the method Withdraw. The message ba.Withdraw(amount) withdraws recursively half of the amount from the left part of the pyramid and recursively half of the amount from the right part of the pyramid. In case of a leaf account, ordinary simple withdrawing is used.
  • Program a method DistributeEven which distributes the total amount of money evenly on all accounts in the pyramid.


Collected references
Contents Index
Value types Later in these notes
Similar illustration of value types Later in these notes
Interfaces Later in these notes
Delegates in C# Later in these notes
Cloning Later in these notes
Overriding Equals in C# Later in these notes
ReferenceEquals in class Object MSDN2 API Documentation
Equals in class Object MSDN2 API Documentation
Reference types Earlier in these notes
Similar illustration of reference types Earlier in these notes
The struct System.TimeSpan MSDN2 API Documentation
The struct System.DateTime MSDN2 API Documentation
Constructors in classes Earlier in these notes
The top of the C# class hierarchy Later in these notes
Similar example with classes Earlier in these notes
Visibility Issues Earlier in these notes
Visibility of members in types Earlier in these notes
Brad Appleton: Patterns and Software: Essential Concepts and Terminology Internet Resource
Design Patterns in C# from dofactory.com Internet Resource
Dice and Random objects Earlier in these notes
The original Die example Earlier in these notes
MSDN: Implementing Singleton in C#
Factory design patterns Later in these notes

 

Chapter 4: Reference types, Value types, and Patterns
Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
Generated: February 7, 2011, 12:14:05