76

I've got the following (simplified):

interface IFindFilesObserver
{
    void OnFoundFile(FileInfo fileInfo);
    void OnFoundDirectory(DirectoryInfo directoryInfo);
}

class FindFiles
{
    IFindFilesObserver _observer;

    // ...
}

...and I'm conflicted. This is basically what I would have written in C++, but C# has events. Should I change the code to use events, or should I leave it alone?

What are the advantages or disadvantages of events over a traditional observer interface?

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380

11 Answers11

80

Consider an event to be a callback interface where the interface has only one method.

Only hook events you need
With events, you only need to implement handlers for events you're interested in handling. In the observer interface pattern, you'd have to implement all methods in the entire interface including implementing method bodies for notification types you don't actually care about handling. In your example, you always have to implement OnFoundDirectory and OnFoundFile, even if you only care about one of these events.

Less maintenance
Another good thing about events is you can add a new one to a particular class so that it will raise it, and you don't have to change every existing observer. Whereas if you want to add a new method to an interface, you have to go around every class that already implements that interface and implement the new method in all of them. With an event though, you only need to alter existing classes that actually want to do something in response to the new event you're adding.

The pattern is built into the language so everybody knows how to use it
Events are idiomatic, in that when you see an event, you know how to use it. With an observer interface, people often implement different ways of registering to receive notifications and hook up the observer.. with events though, once you've learnt how to register and use one (with the += operator), the rest are all the same.

Pros for interfaces
I haven't got many pros for interfaces. I guess they force someone to implement all methods in the interface. But, you can't really force somebody to implement all those methods correctly, so I don't think there's a lot of value on this.

Syntax
Some people don't like the way you have to declare a delegate type for each event. Also, standard event handlers in the .NET framework have these parameters: (object sender, EventArgs args). As sender doesn't specify a particular type, you have to down-cast if you want to use it. This often is fine in practice, it feels not quite right though because you're losing the protection of the static type system. But, if you implement your own events and don't follow the .NET framework convention on this, you can use the correct type so potential down-casting isn't required.

Pang
  • 9,564
  • 146
  • 81
  • 122
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • 2
    A small pro for interfaces is that when you have a lot of events that usually come together you don't have to register them all in a huge block of event +=s, but only register that one interface. I still prefer events though, of course :) – configurator Feb 15 '09 at 14:30
  • One con for events is that they don't fire across app domains. If you app is (or will be) complex, this will become an issue. – TMN Oct 27 '10 at 12:56
  • 16
    Events are good for frameworks, because they usually don't change often and by the time people start using them they should be relatively stable. This might not be the case when developing an application with quickly changing specifications. Suppose a class's behavior changes and requires a new event. You'll need to examine each and every usage of the class. Using an observer interface the compiler will inform you where attention is required, but if you use events you'll have a much more difficult time. – Christo Apr 27 '11 at 17:57
  • 3
    Little known fact; you actually don't have to declare a delegate type for events. You can just use `event Action` or `event Action` etc. – natli Jul 18 '13 at 13:35
  • 3
    I disagree with your point against interfaces stating that interfaces make you implement every method/prop that is defined on the interface as this is clearly a violation of the interface segregation principle. Having well defined single purpose interfaces will not lead to this exact problem that you've mentioned – Operatorius Sep 19 '18 at 18:50
  • 1
    @Operatorius You could split the interface in the question into IFileFound and IFolderFound interfaces, but that seems like a proliferation of interfaces. You'd be better with events instead of interfaces for each of those, wouldn't you. – Scott Langham Sep 20 '18 at 12:48
  • What about async/await? Events seem like they make that more difficult – Joe Phillips Nov 01 '19 at 14:53
  • I know 2009 to 2023 is a lot, but I'm intersted in the subject. It seems most of complains there, are for bad implementation of interfaces. Apart from that, is proliferation of interfaces all that bad? May be not for everything, but it seems more complexes scenarios would benefit of the clarity of interfaces instead of events. Is the answer's owner opinion still the same? – E. Zacarias Mar 30 '23 at 20:16
  • 1
    @E.Zacarias For the example given in the question, I still think events are the C# way to do it. In my code I have many interfaces, and interfaces are good. The best choice depends on your specific scenario, you should try both and find out what works for you. – Scott Langham Mar 31 '23 at 22:48
32

Hmm, events can be used to implement the Observer pattern. In fact, using events can be regarded as another implementation of the observer-pattern imho.

Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • 13
    Absolutely. This is a bit like asking, "Should I implement the iterator pattern or use foreach and IEnumerable?" – Jon Skeet Feb 15 '09 at 13:08
  • Events being fired from your observer is a natural way to notify multiple consumers of a change in the application – JoshBerke Feb 15 '09 at 14:01
  • 12
    I know that this is the observer pattern; I asked whether I should implement the pattern using a callback interface or by using events. – Roger Lipscombe Dec 08 '09 at 16:47
  • I believe using events usually a better options because by default it decouple both components and indirectly supports to have more than one source of events in future without changing any code. – Rahul Garg May 12 '17 at 19:08
13
  • Events are harder to propagate through chain of objects, for example if you use FACADE pattern or delegate work to other class.
  • You need to be very careful with unsubscribing from events to allow object to be garbage collected.
  • Events are 2x time slower than simple function call, 3x slower if you do null check on every raise, and copy event delegate before null check and invocation to make it thread safe.

  • Also read MSDN about new (in 4.0) IObserver<T> interface.

Consider this example:

using System;

namespace Example
{
    //Observer
    public class SomeFacade
    {
        public void DoSomeWork(IObserver notificationObject)
        {
            Worker worker = new Worker(notificationObject);
            worker.DoWork();
        }
    }
    public class Worker
    {
        private readonly IObserver _notificationObject;
        public Worker(IObserver notificationObject)
        {
            _notificationObject = notificationObject;
        }
        public void DoWork()
        {
            //...
            _notificationObject.Progress(100);
            _notificationObject.Done();
        }
    }
    public interface IObserver
    {
        void Done();
        void Progress(int amount);
    }

    //Events
    public class SomeFacadeWithEvents
    {
        public event Action Done;
        public event Action<int> Progress;

        private void RaiseDone()
        {
            if (Done != null) Done();
        }
        private void RaiseProgress(int amount)
        {
            if (Progress != null) Progress(amount);
        }

        public void DoSomeWork()
        {
            WorkerWithEvents worker = new WorkerWithEvents();
            worker.Done += RaiseDone;
            worker.Progress += RaiseProgress;
            worker.DoWork();
            //Also we neede to unsubscribe...
            worker.Done -= RaiseDone;
            worker.Progress -= RaiseProgress;
        }
    }
    public class WorkerWithEvents
    {
        public event Action Done;
        public event Action<int> Progress;

        public void DoWork()
        {
            //...
            Progress(100);
            Done();
        }
    }
}
Alex Burtsev
  • 12,418
  • 8
  • 60
  • 87
8

Pros of an interface-solution:

  • If you add methods, existing observers needs to implement those methods. This means that you have less of a chance of forgetting to wire up existing observers to new functionality. You can of course implement them as empty methods which means you have the luxury of still doing nothing in response to certain "events". But you won't so easily forget.
  • If you use explicit implementation, you'll also get compiler errors the other way, if you remove or change existing interfaces, then observers implementing them will stop compiling.

Cons:

  • More thought has to go into planning, since a change in the observer interface might enforce changes all over your solution, which might require different planning. Since a simple event is optional, little or no other code has to change unless that other code should react to the event.
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
8

Some further benefits of events.

  • You get proper multicast behaviour for free.
  • If you change the subscribers of an event in response to that event the behaviour is well defined
  • They can be introspected (reflected) easily and consistently
  • Tool chain support for events (simply because they are the idiom in .net)
  • You get the option to use the asynchronous apis it provides

You can achieve all of these (except the tool chain) yourself but it's surprisingly hard. For example: If you use a member variable like a List<> to store the list of observers. If you use foreach to iterate over it then any attempt to add or remove a subscriber within one of the OnFoo() method callbacks will trigger an exception unless you write further code to deal with it cleanly.

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
5

The best way to decide is this: which one suits the situation better. That might sound like a silly or unhelpful answer, but I don't think you should regard one or the other as the "proper" solution.

We can throw a hundred tips at you. Events are best when the observer is expected to listen for arbitrary events. An interface is best when the observer is expected to listed to all of a given set of events. Events are best when dealing with GUI apps. Interfaces consume less memory (a single pointer for multiple events). Yadda yadda yadda. A bulleted list of pros and cons is something to think about, but not a definitive answer. What you really need to do is try both of them in actual applications and get a good feel for them. Then you can choose the one that suits the situation better. Learn form doing.

If you have to use a single defining question, then ask yourself which better describes your situation: A set of loosely related events any of which may be used or ignored, or a set of closely related events which will all generally need to be handled by one observer. But then, I'm just describing the event model and interface model, so I'm back at square one: which one suits the situation better?

snarf
  • 2,684
  • 1
  • 23
  • 26
  • 1
    That's why I asked for pros/cons. I realise that there's no "one size fits all" solution. It was just something I was weighing up and thought it'd be an interesting question. – Roger Lipscombe Feb 15 '09 at 17:38
3

Pros are that events are more 'dot-netty'. If you are designing non-visual components that can be dropped onto a form, you can hook them up using the designer.

Cons are that an event only signifies a single event - you need a separate event for each 'thing' that you want to notify the observer about. This doesn't really have much practical impact except that each observed object would need to hold a reference for every observer for every event, bloating memory in the case where there are lots of observed objects (one of the reasons they made a different way of managing the observer/observable relationship in WPF).

In your case I'd argue it doesn't make much difference. If the observer would typically be interested in all those events, use an observer interface rather than separate events.

U62
  • 4,355
  • 1
  • 24
  • 37
  • Well there's nothing stopping you to have a single c# event that differentiates different events by a child class of EventArgs - I wouldn't do that though. – Tamas Czinege Feb 15 '09 at 12:57
2

A benefit of interfaces is that they are easier to apply decorators to. The standard example:

subject.RegisterObserver(new LoggingObserver(myRealObserver));

compared to:

subject.AnEvent += (sender, args) => { LogTheEvent(); realEventHandler(sender, args); };

(I'm a big fan of the decorator pattern).

Andreas
  • 1,200
  • 2
  • 13
  • 24
  • It is just as easy to apply the decorator pattern over an interface. Just use a productivity tool with your IDE if all the typing becomes an issue. – Christo Apr 27 '11 at 18:05
2

I prefer an event base solution for the following reasons

  • It reduces the cost of entry. It's much easier to say "+= new EventHandler" than to implement a full fledged interface.
  • It reduces maintenance costs. If you add a new event into your class that's all that needs to be done. If you add a new event to an interface you must update every single consumer in your code base. Or define an entirely new interface which over time gets annoying to consumers "Do I implement IRandomEvent2 or IRandomEvent5?"
  • Events allow for handlers to be non-class based (ie a static method somewhere). There is no functional reason to force all event handlers to be an instance member
  • Grouping a bunch of events into an interface is making an assumption about how the events are used (and it's just that, an assumption)
  • Interfaces offer no real advantage over a raw event.
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
2

Java has language support for anonymous interfaces, so callback interfaces are the thing to use in Java.

C# has support for anonymous delegates - lambdas - and so events are the thing to use in C#.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
1

If your objects will need to be serialized in some way that retains references such as with NetDataContractSerializer or perhaps protobuf events will not be able to cross the serialization boundary. Since observer pattern relies on nothing more than just object references, it can work with this type of serialization with no problem if that is what is desired.

Ex. You have a bunch of business objects that link to each other bidirectionally that you need to pass to a web service.

jpierson
  • 16,435
  • 14
  • 105
  • 149