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 observers
to guard against this condition.
See the following pages, linked from our course home page:
Typically we measure a program's size in the number of lines that it contains. The cloc utility is useful for counting lines in any program.
In the lecture we examined various real-world programs to see how large they are, and which languages they are written in. The line counts here were measured by cloc, and don't include blank lines or comments:
gnome-mines (a Minesweeper clone in GTK): 1,700 lines of Vala
gnome-terminal: 18,000 lines of C++
Pinta (a graphics editor): 36,000 lines of C#
Nautilus (the file manager on the GNOME desktop): 88,000 lines of C
GTK: 614,000 lines of C
Visual Studio Code: 920,000 lines of TypeScript
Firefox: 6.7M lines of JavaScript, 5.2M lines of C++, 2.6M lines of C, 2.5M lines of Rust
Linux kernel: 15.3M lines of C
As you can see, many programs are larger than you might think. Also, most programs grow continuously over time as developers add more features.
As a rough estimate, a good programmer might write 1,000 lines of code in a month. So a program with 1,000,000 lines of code might take something like 1,000 person-months = 83 person-years of time to write. Probably no single person can understand every line in a program that large.