An event is a class member that lets callers register event handlers that will receive notifications. Each event handler is a delegate. When an event is raised (i.e. fires), a notification is sent to each registered event handler. Each notification includes arguments matching the event's delegate type.
Events are useful for implementing the observer pattern, in which one or more observers may want to hear about changes to an object. A common example of this pattern is a model-view architecture, in which the view observes the model and displays the model's data. In such an architecture we want the model to be unaware of the view. Using an event, a view can register to find out when the model has changed, without giving the model specific knowledge of the view class.
Here's an array class including an event that is raised whenever any array element changes:
delegate void Notify(int index, int old, int now);
class WatchableArray {
int[] a;
public WatchableArray(int n) {
a = new int[n];
}
public event Notify? changed;
public int this[int i] {
get => a[i];
set {
int prev = a[i];
a[i] = value;
if (changed != null)
changed(i, prev, a[i]); // fire the event to notify observers
}
}
}Notice that the event declaration includes a delegate type, and that we can raise an event using method call syntax.
Use the += operator to register a
handler with an event. For example, we can create an instance of the
WatchableArray class and register an event handler:
void onChange(int index, int old, int now) {
WriteLine($"a[{index}] changed from {old} to {now}");
}
WatchableArray a = new(5);
a.changed += onChange;
…
If some method later calls
a[3] = 4;
then the above event handler will run, and will print a message such as
a[3] changed from 0 to 4
Be warned: if you attempt to raise
an event that has no registered handlers, you will get a
NullPointerException. In my opinion this is a weakness
in the C# event system: if would be nicer if raising such an event
did nothing. However, this is how it works. So in the example above,
we need to write
if (changed != null)
changed(i, prev, a[i]); // fire the event to notify observersto guard against this condition. Alternatively, you may write
changed?.Invoke(i, prev, a[i]); // fire the event to notify observers
The ?. operator (the null-conditional operator)
calls a method only if the operand on the left is not null.
See this list of libraries and the platforms they run on.
See the following pages, linked from our course home page:
In the lecture we wrote a C# program to play the game of Sokoban using GTK 4. Our program lets the user play the game using the arrow keys, and can also solve levels automatically using a breadth-first search.
Here is a Git repository with the code we wrote.