In one of his articles on building a CAB-like infrastructure, Jeremy Miller brought out a pretty interesting point: The choice between using Events/Delegates and Direct Method Calls. This can be a controversial topic at hand, but it's an interesting aspect, and can significantly affect the readability of the code.
Events and delegates are very powerful features I use all the time. However, here's a tip: Be careful about building an entire framework or complex structure based purely around delegate calls. I've been spending a lot of time the past few days tracing, debugging and running through the code in the Base Adapter sample code in the BizTalk Server 2004 SDK , which is exactly a little framework for building BizTalk Adapters.
Now, the base adapter is OK, in that it does make it a bit faster to develop adapters by taking care of a lot of the boilerplate code necessary. However, a significant part of it is built out of classes that use a lot of delegates (not even proper events, unfortunately) to notify of significant events around each other; like for example that a batch was submitted to BizTalk or that a message needs to be suspended.
The unfortunate side effect of this is that tracing the base adapter code in source code form requires a lot of patience and a lot of Find References/Find Definition jumps all around to figure out exactly what's going on. In fact, I've spent quite a few hours on it and the code can be significantly convoluted; and parts of it are just a lot easier to trace using Step Though/Step Into in the debugger; which is rather frustrating.
So next time you want to build a framework or library and think gluing all together with delegates and events is cool; make sure you've got a real use case for them and that they are not going to end up hindering readability and maintainability.
 The version included in the BizTalk 2006 SDK is different and a lot simpler. I still don't quite like it and generally prefer to write directly against the Adapter Framework APIs, but that's just me.