23.3. More Features

23.3.1. Delegates and Events

Java's inner classes (in particular anonymous ones) took me a while to understand when they were introduced with JDK 1.1 as part of the new event handling. Being spoiled by Python's "method objects", they seem like a rather complex way to handle callback functions. C#'s solution resembles Eiffel's agents, but with more syntax support.

class Test {
  public delegate double DoubleFunction(double x);

  static void Evaluate(DoubleFunction f, double x) {
    System.Console.WriteLine("f(" + x + ")=" + f(x));
  }

  static double Times2(double x) { return 2*x; }

  static void Main() {
    Evaluate(new DoubleFunction(Times2), 5.5);
  }
}

A delegate is a function type. In the example, we define the type DoubleFunction for functions with a single double argument and returning a double. We can now use this new type just like any other type to declare variables and method arguments as we have done in the Evaluate method. Calling a delegate looks just like a function call. There is no automatic transformation from a method to a delegate. Instead, we create the delegate (or function object) by passing the name of the method to the constructor of the delegate.

The previous example used static methods only, but we can do the same with instance methods.

class Test {
  public delegate double DoubleFunction(double x);

  static void Main() {
    LinearFunction f = new LinearFunction(2, 3);
    Evaluate(new DoubleFunction(f.Compute), 5.5);
  }

  static void Evaluate(DoubleFunction f, double x) {
    System.Console.WriteLine("f(" + x + ")=" + f(x));
  }
}

class LinearFunction {
  private double a, b;

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

  public double Compute(double x) {
    return a*x + b;
  }
}

result:
f(5.5)=14

Here, we define a class LinearFunction modeling scalar linear functions of the form f(x)=a*x+b. In the main program, we first construct a specific instance of a linear function and then pass its Compute method wrapped into the DoubleFunction delegate to the evaluation method.

For now, delegates look just like function objects, but C# goes a little further and allows us to add and subtract delegates. What are these operations supposed to mean for functions?

class Test {
  public delegate int Printer(string message);
    
  static void Main() {
    Printer print1 = new Printer(Print1);
    Printer print2 = new Printer(Print2);
    Printer printer = print1 + print2;
    int result = printer("Hello World");
    System.Console.WriteLine("result=" + result);

    printer -= print1;
    result = printer("Hello World");
    System.Console.WriteLine("result=" + result);
  }

  static int Print1(string message) {
    System.Console.WriteLine("Print1: " + message);
    return 1;
  }

  static int Print2(string message) {
    System.Console.WriteLine("Print2: " + message);
    return 2;
  }
}

result:

Print1: Hello World
Print2: Hello World
result=2
Print2: Hello World
result=2

When we add two delegates, and call the result, the two functions are executed one after the other. The return value of the combined delegate is the return value of the last function executed. Similarly, we can subtract a delegate from the sum and end up with the remaining one.

The main application of function objects in Windows programs are callbacks for graphical user interfaces. Their design often follows the observer (publish-subscribe) pattern. Hence, the "delegate arithmetic" is interpreted as adding or removing a function from a list of callbacks.

You may wonder at this point (like I did) what happens if we try and subtract the other delegate as well. The subtracting itself is carried out without any complaint, but calling the result causes a null reference exception.

C# gives the observer pattern its place in the syntax with the introduction of events. Besides attributes and methods, events are the third kind of member a class may have. An event wraps a delegate and presents it differently to the class itself and the outside world. From the outside, we can only add and remove callbacks with the += and -= operators. Inside of the class containing the event, we can "fire" the event by calling it just like a delegate.

class Test {
  public delegate int Printer(string message);
  public event Printer OnPrint;

  static void Main() {
    new Test().run();
  }

  public void run() {
    Printer print1 = new Printer(Print1);
    Printer print2 = new Printer(Print2);
    
    OnPrint += print1;
    OnPrint += print2;
    System.Console.WriteLine("result=" + OnPrint("Hello World"));

    OnPrint -= print2;
    System.Console.WriteLine("result=" + OnPrint("Hello World"));


    OnPrint -= print1;
    if (OnPrint == null) {
      System.Console.WriteLine("empty callback list");
    }
  }
  ...
}

result:

Print1: Hello World
Print2: Hello World
result=2
Print1: Hello World
result=1
empty callback list

The event is declared in the class like a delegate attribute, but with the keyword event as an additional modifier. Unfortunately, we can not call an event if it has no subscribers. Doing so will result again in a null reference exception. However, we can prevent this situation, since an empty event evaluates to null.

23.3.2. Operator Overloading

As we have noticed already, C# keeps a lot more of the C++ features than Java. One more example is operator overloading. The underlying principle is that we should be able to define our own types which look and work the same way as the built-in types such as integers and strings. To this end, we need to define the meaning of operators for our own types.

public class Point {
  private double x, y;

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

  public static Point operator+(Point a, Point b) {
    return new Point(a.x + b.x, a.y + b.y);
  }

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

public class Test {
  static void Main() {
    Point a = new Point(1.5, 2.5), b = new Point(2.5, 3.5);
    Point c = a + b;
    System.Console.WriteLine("c=" + c);
  }
}

23.3.3. Casting References

In a strongly typed object-oriented language (especially without generics), we are often forced to cast a reference. And although not proper OO-style, we also encounter situations where we need to check the type of an object and, if it is the expected type, cast the reference so that we can handle the special type. C# offers the two convenient operators is and as for this reason.

class Hello {
  static void Main() {
    object o = "hello";

    if (o is string) {
      System.Console.WriteLine("this is a string");
    }

    string s = o as string;

    if (s == null) {      
      System.Console.WriteLine("not a string");
    }
    else {
      System.Console.WriteLine("s={0}", s);
    }    
  }
}

this is a string
s=hello

The is operator is the crisp version of Java's instanceof.The as operator combines the type check with the casting (thus saving one type check). If the reference is of the expected type, it is cast to this type. Otherwise the as operator return a null reference.

23.3.4. Enumerated Types

Enumerated types are another example of a C/C++ feature which Java dropped to keep the language simple. C# follows Ada in supporting enumerated types as first class types. An enumerated type is defined like in C, but the values are not just integers in disguise, but objects which allow us to get the numeric value as well as the string representation.

using System;
class Hello {
  enum Color {
    Red, Green, Blue
  }
  static void Main() {
    foreach (Color color in Enum.GetValues(typeof(Color))) {
      Console.WriteLine("color: {0} {1}", (int)color, color.ToString());
    }

    Color c = (Color)Enum.Parse(typeof(Color), "Green");
    Console.WriteLine("c: {0}", c.ToString());
  }
}
color: 0 Red
color: 1 Green
color: 2 Blue
c: Green

The loop walks through all the values of out enumerated type, which we obtain by applying the static GetValues method to the Color type. The print statement shows the conversion of the color value to an integer and a string. The last two lines demonstrate the opposite direction convertion a string to a Color value using the Parse method.

By adding the Flags attribute we can change the semantics of the ToString and Parse methods so that the enumerated type behaves like a bit set.

using System;
class Hello {
  [Flags]
  enum Permission {
    Read = 0x1, Write = 0x2, Execute = 0x4
  }
  static void Main() {
    Console.WriteLine(
      "permissions: {0}", Permission.Read | Permission.Execute);

    Permission p = (Permission)Enum.Parse(typeof(Permission), "Read, Write");
    Console.WriteLine("p: {0}", p.ToString());
  }
}
permissions: Read, Execute
p: Read, Write

The .NET framework library contains many examples for this kind of enumeration (e.g., FileAttributes).

23.3.5. Releasing Resources

Like in any programming environment with garbage collection we can't rely on destructors to release resources such as file handles or network connections, since we do not know when the destructors are called (if they are called at all). We therefore have to ensure manually that our resources are closed under all circumstances. In Java, this leads to the idiom of a try-finally statement which works as well in C#.

using System.IO;

class Hello {
  static void Main() {
    StreamWriter writer = null;
    try {
      writer = new StreamWriter("hello.txt", false);
      writer.WriteLine("Hello World");
    }
    finally {
      if (writer != null) {
	writer.Close();
      }
    }
  }
}

Besides the required number of lines it is disturbing that we have to widen the scope of the writer variable only to be able to close it in the finally block. Fortunately, C# has a special syntax (similar to Lisp's with-open-file) which takes care of the closing of resources automatically just like a destructor in C++ would do.

using System.IO;

class Hello {
  static void Main() {
    using (StreamWriter writer = new StreamWriter("hello.txt", false)) {
      writer.WriteLine("Hello World");
    }
  }
}

The using statement creates a new scope for the variable writer demarcated by the curly braces. When this scope is left, whether by successfully executing the block or by throwing an exception, the Dispose method of the declared object is called. The mechanism works for all objects implementing the IDisposable interface. All the classes representing system resources such as files, sockets, database connections in the .NET framework library implement this interface.

23.3.6. Preprocessor

Among all the features carried over from C/C++ to C#, I considered the preprocessor the least likely candidate. C# supports a small subset of C's preprocessor directives, mainly to allow for conditional compilation.

#define DEBUG

class Hello {
  static void Main() {
#if DEBUG
    System.Console.WriteLine("Debug");
#else
    System.Console.WriteLine("Non-Debug");
#endif
  }
}
Debug

The preprocessor lets us define (only at the very beginning of the source file) symbols such as DEBUG, check them with the #if preprocessor directive (including #elif and #else), and undefine them with #undef. Preprocessor symbols can also be set from the ouside using the -define: (or -d: for short) option of the C# compiler. This kind of conditional compilation is one of the things I have missed about Java when dealing with the (apparently unavoidable) uncompatible API changes.

Besides defining and checking symbols, the only other preprocessor directive is #region/#endregion. It replaces the special comments in VC++ code marking code regions that are generated and managed by a designer tool such as Visual Studio.

23.3.7. Reflection and Attributes

From the early days of Smalltalk, relection, that is, the ability to obtain information at runtime about classes and their members, has been an important when writing generic software. As an example, consider how easy it is to serialize an object or map it to a relational database in a language supporting reflection. In C#, we can ask an object for its type and obtain all kinds of information about the type.

using System;
using System.Reflection;

class Person {
  private string name;

  public Person(string name) {
    this.name = name;
  }

  public string Name {
    get { return this.name; }
  }
}

class Test {
  static void ShowMembers(Type type) {
    MemberInfo[] members = type.FindMembers
      (MemberTypes.All,
       BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
       null, null);
    foreach (MemberInfo member in members) {
      Console.WriteLine("{0}: {1}", member.MemberType, member.Name);
    }
  }

  static void Main() {
    Person person = new Person("Homer");
    ShowMembers(person.GetType());
  }
}
Constructor: .ctor
Field: name
Method: get_Name
Method: Equals
Method: Finalize
Method: GetHashCode
Method: GetType
Method: MemberwiseClone
Method: ToString
Method: obj_address
Method: FieldGetter
Method: FieldSetter
Property: Name

Instead of providing individual methods to obtain information about the various types of members (C# has properties and events in addition to Java's fields, methods, and constructors), C# offers the strongly parameterizable FindMembers method. The first argument specifies the kind (or kinds) of members we are looking for. The second argument filters depending on modifiers or binding of the member. In the example, we are looking for all instance members and omit the class members. The last two arguments (in the example set to null) allow us to pass an additional filter function (a delegate) and argument passed to this function when checking a member.

Once we have this information we can use it to access members dynamically. Here is an example for getting and setting a field using reflection (often used for generic services such as serialization or database mapping).

using System;
using System.Reflection;

class Person {
  private string name;

  public Person(string name) {
    this.name = name;
  }

  public string Name {
    get { return this.name; }
  }
}

class Test {
  static void UseNameField(object obj) {
    Type type = obj.GetType();
    FieldInfo nameField = (FieldInfo)type.FindMembers
      (MemberTypes.Field,
       BindingFlags.Instance | BindingFlags.NonPublic,
       Type.FilterName, "name")[0];
    nameField.SetValue(obj, "Bart");
    Console.WriteLine("name: {0}", nameField.GetValue(obj));
  }

  static void Main() {
    Person person = new Person("Homer");
    UseNameField(person);
  }
}
name: Bart	  
	

Here we use one of the pre-defined filter functions, Type.FilterName to get just the name field. A the main difference between C#'s FieldInfo and Java's Field, C# does not provide specialized getters and setters for the primitive types. This implies that access to primitive fields using reflection always causes boxing or unboxing (or both).

C# (and the .NET framework in general) take the idea of reflection one step further and allow us to attach any kind of meta data to types and their members as part of the program. Just like the other meta data, this information is available at runtime.

Attributes are specified in square brackets right before the component (class, field, etc.) they belong to. Basically, the attribute definitions are calls to the constructors of the underlying attribute classes. Positional arguments are directly passed to the constructor, and keyword arguments cause the associated properties to be set. The following example defined a new custom attribute which can be used to add a label and description (e.g., for dynamically created user interfaces) to a field.

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Field)]
class DisplayAttribute : Attribute {
  private string label;
  private string description;

  public DisplayAttribute(string label) {
    this.label = label;
  }

  public string Label {
    get { return this.label; }
  }
  public string Description {
    get { return this.description; }
    set { this.description = value; }
  }
}

class Person {
  [Display("My name is", Description="Name of the person")]
  private string name;

  public Person(string name) {
    this.name = name;
  }

  public string Name {
    get { return this.name; }
  }
}
My name is: Bart (Name of the person)	  
	

As an aside, you can of course achieve the same effect in the dynamic languages such as Lisp or Python. In Python, for example, you can add arbitrary fields to classes, functions, and methods.