Ayende @ Rahien

My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:


+972 52-548-6969

, @ Q c

Posts: 6,130 | Comments: 45,558

filter by tags archive

API Design

time to read 2 min | 216 words

There are several important concerns that needs to be taken into account when designing an API. Clarity is an important concern, of course, but the responsibilities of the users and implementers of the API should be given a lot of consideration. Let us take a look at a couple of designs for a simple notification observer. We need to observe a set of actions (with context). I don't want to have force mutable state on the users, so I have started with this approach (using out parameters instead of return values in order to name the parameter):

public interface INotificationObserver
    void OnNewSession(out object sessionTag);
    void OnNewStatement(object sessionTag, StatementInformation statementInformation, out object statementTag);
    void OnNewAction(object statementTag, ActionInformation actionInformation);

I don't really like this, too much magic objects here, and too much work for the client. We can do it in a slightly different way, however:

public delegate void OnNewAction(ActionInformation actionInformation);

public delegate void OnNewStatement(StatementInformation statementInformation, out OnNewAction onNewAction);

public interface INotificationObserver
    void OnNewSession(out OnNewStatement onNewStatement);



What's exactly the point in not using events here?

You could easily create three events: NewSessionCreated, NewStatementCreated and NewActionInvoked. Then any client can use an arbitrary subset of these events to implement his desired behaviour...

I have to admit that I never understood the need for the observation pattern in c#, for me it's integrated in the language.

Andrey Shchekin

Your solution is tailored for a specific use case that you envision, and that specific use case is probably easier done your way. However, the user will also have to understand and conform to your use case, which may be a problem.

I agree with Frederik:

event EventHandler <sessioneventargs NewSession;

event EventHandler <statementeventargs NewStatement;

event EventHandler <actioneventargs NewAction;

// names can be better, but I do not know what they actually do

So you can handle all actions without even thinking on what a session is. If you want to handle them in a specific way for a specific statement, you can have NewAction event on the StatementContext available through the StatementEventArgs.

Andrey Shchekin

Sorry, the blog ate my generics.

event EventHandler NewSession;

event EventHandler NewStatement;

event EventHandler NewAction;

Ayende Rahien

Frederik & Andrey,

The problem is state. I don't want to just just any old NewStatement, I want to call NewStatement for a particular session


And which state exactly can't you add to the event as EventArgs?


The general pattern for using EventArgs is to pass the "state" data to the constructor and refer to that data using read only properties.

This effectively makes EventArgs (and its properly designed descendants) immutable and therefore, I think, suitable for the scenarios you're envisioning.

There are, of course, some exceptions to this rule (namely, CancelEventArgs), but they are few and far between, target a different use case and do not inhibit using EventArgs correctly otherwise.

You don't have to use events and EventArgs; you can use a similar pattern if you want, as long as you pass around an immutable state-holding object.

On the other hand, using out arguments in the API forces the programmer to create variables that clutter up the code and may not be needed to begin with.

They also make the code difficult to maintain when (not if) the API changes because there's no encapsulation, as with EventArgs.

Another disadvantage is that they inhibit fluent programming.

In my experience, out arguments are either used in "Try" kind of statements for safe conversions or other scenarios where exceptions are ignored, or in opaque difficult-to-document and non-standard scenarios. While your example specifically does not seem to fall into either category, usually they do, and probably should not be recommended as a programming style.

Anyway, that's my take. I'd be happy to hear a contrary opinion...

Andrey Shchekin

__I don't want to just just any old NewStatement, I want to call NewStatement for a particular session.

That's easy, just add NewStatement event to a SessionContext provided in a SessionEventArgs.

Ayende Rahien

Yes, I can.

However, I find the usage of the out parameter make it explicit that I have to fill in the value, and make the purpose of the code clearer.

It is actually a delegate which has another out parameter for a deeper layer in the code, which also maintains state. Seems simpler that way to me

Andrey Shchekin

Do you __have to subscribe to all of them?

What if you do not care about Actions?

It probably depends on what observer is for.

Ayende Rahien

I my scenario, yes I do. There is a well defined contract between the observer and the observee.

Comment preview

Comments have been closed on this topic.


  1. How to waste CPU and kill your disk by scaling 100 million inefficiently - 13 hours from now
  2. RavenDB Conference 2016–Slides - about one day from now

There are posts all the way to Jun 01, 2016


  1. The design of RavenDB 4.0 (14):
    26 May 2016 - The client side
  2. RavenDB 3.5 whirl wind tour (14):
    25 May 2016 - Got anything to declare, ya smuggler?
  3. Tasks for the new comer (2):
    15 Apr 2016 - Quartz.NET with RavenDB
  4. Code through the looking glass (5):
    18 Mar 2016 - And a linear search to rule them
  5. Find the bug (8):
    29 Feb 2016 - When you can't rely on your own identity
View all series


Main feed Feed Stats
Comments feed   Comments Feed Stats