4

Now the title might seem like the question has been asked before but let me explain my situation and you'll see why I am asking this question.

Let's consider this interface :

public interface IListChangedListener {
    public event EventHandler ItemAdded;
    public event EventHandler ItemRemoved;
}

If someone wants to implement this interface, they would have to check if ItemAdded is null and only if it is not null, they would call it. Well, I want to simplify their job by giving an in built function which will do the same. Just calling that function will check if the corresponding event is null, and if it isn't, it'll fire its corresponding event.

The problem is, this is an interface. So I can't define a method. So I thought I might go with an abstract class. But considering the situation, it would be best suited to make this as an interface rather than an abstract class.

So do I just drop the idea of giving an in built function? Or do I make it an abstract class? Which better suits the situation? I am having a tough time deciding which one to use. Some help please?

  • On a totally different note, is the way I named my interface correct (according to the guidelines and all that)? – Rakshith Ravi Mar 15 '15 at 16:59
  • Who implements this interface? The collection, or some object interested in changes? – Krzysztof Kozielczyk Mar 15 '15 at 17:59
  • Some object interested in changes – Rakshith Ravi Mar 15 '15 at 19:21
  • Why do you need the events in this case? Why not simply make the interface have OnItemAdded and OnItemRemoved methods that would be called by the collection directly? – Krzysztof Kozielczyk Mar 15 '15 at 23:13
  • 2
    It's kind of confusing that your interface is called ListChangedListener, but appears to fire events rather than listen. – Mr Cochese Mar 17 '15 at 10:51
2

What would be best is to have a mixin, but unfortunately C# doesn't include this concept. In the absence of mixins, I'd pick an interface over an abstract class almost every time, and definitely in this case. Whatever functionality has to be offered to derived classes, it can be offered in form of helper objects and utility functions. Favoring composition over inheritance is my favorite design principle.

In this specific case we should be able to have it both though. I would definitely have the interface, as you defined it. The interface should be used by all consumers and other classes in your utility. For the convenience of your users (developers) I would also have a default implementation of the interface that can be used as a base class.

public interface IListChangedListener {
    public event EventHandler ItemAdded;
    public event EventHandler ItemRemoved;
}

public class ListChangedListener : IListChangedListener {
    public event EventHandler ItemAdded;
    public event EventHandler ItemRemoved;

    public void FireItemAdded(...) {
        if (ItemAdded != null)
            ItemAdded(...)
    }

    public void FireItemRemoved(...) {
        if (ItemRemoved != null)
            ItemRemoved(...)
    }
}

This way a developer can either derive from the ListChangedListener class, or implement the IListChangedListener interface directly, depending on circumstances. In the end of the day what counts is that the listener class they provide implements the IListChangedListener interface.

Would that work?

  • Seems good. From my understanding, you mean to say provide a default implementations and an interface? So if the developer needs to inherit from another class, he'd use the interface as opposed to the class? Not a bad idea. Providing both is possible. But another question arises : is it worth it? Would it seem too over complicated? – Rakshith Ravi Mar 17 '15 at 17:40
  • Yes, that's what I'm suggesting. I'd say it is worth it, as you're not forcing the developer to use the default implementation, it is merely there for convenience. Of course it all depends on if you're expecting the developers to have to implement the interface often. If implementing of the interface is necessary to use your library, I'd make it as easy and flexible as possible. If a developer only implements this interface if they want to use some obscure feature of your library, I would hesitate. – Krzysztof Kozielczyk Mar 17 '15 at 18:15
3

As usual:

  • If you need to implement some methods, the interface is not a solution. You have to use an abstract class.

  • On the other hand, if you need to describe the behavior, especially in a context where classes which have this behavior might already have a parent class, you have to use an interface, since in C#, a class cannot have multiple parents.

If you need the benefits of both worlds, don't use neither an interface, nor an abstract class, but have a separate object used within the objects.

In your case, you need to describe the behavior. The business logic brought by an eventual abstract class or a dedicated class is not that useful: it's just a null check, nothing more. Therefore, the repetition of those null checks in different classes which implement the interface wouldn't be a serious violation of DRY principle.

Agreed, most implementers will end up creating two methods which check for nullity and raise the event, but again, this is not that complicated to do (nor would the logic change over time).


From the comments, it appears that you are reinventing the wheel. In .NET Framework, there is already a class which does what you need: ObservableCollection<T>.

It has nothing to do with WPF: it is a part of System.Collections.ObjectModel namespace, the one which contains many things related to collections, including Collection<T> itself, ReadOnlyCollection<T>, etc. You can use those collections outside WPF, that is in a console application, a Windows Forms project or a website.

  • So...in my situation, which suits it better? – Rakshith Ravi Mar 15 '15 at 16:38
  • @JohnBrac: what about using what already exists in .NET Framework? (see the edited answer) – Arseni Mourzenko Mar 15 '15 at 16:40
  • I am trying to make my own ObservableCollection<T> class which I will be using in WinForms so I can't use the wpf one. So what do I do? – Rakshith Ravi Mar 15 '15 at 16:41
  • @JohnBrac: System.Collections.ObjectModel.ObservableCollection<T> has nothing to do with WPF. There is no reason you should reinvent the wheel. – Arseni Mourzenko Mar 15 '15 at 16:45
  • Oh! I never knew that! But still. This is not the only case where I am facing this controversy. There are more situations. This was just an example. There are more situations which, unfortunately, due to commercial company code, cannot be cited here. But going by your answer, you mean to say I should use interfaces? – Rakshith Ravi Mar 15 '15 at 16:50
2

You should have both the interface and a class with a default implementation. For example, if you have an IDog interface, it could have a Bark() method defined. A separate Voicebox class could provide the default implementation. This could look like this:

public interface IDog
{
    public void Bark();
}

public class VoiceBox
{
    public void Bark()
    {
        // Do bark
    }
}

class Poodle : IDog
{
    private Voicebox voicebox = Voicebox.GetDefault();

    public Bark()
    {
        voicebox.Bark();
    }
}

This way, the client is not required to use your Voicebox class, but they are still required to make their dog bark somehow. If they want to use your default implementation, then they can use the Voicebox class. Of course, you can make the bark method virtual in the Voicebox class if it needs to be overridden by subclasses.

  • I think you misunderstood my question. What if, like in my situation, there is an event in IDog called OnBark and I need an function which will perform a null check and then call it? A function from voice box can't call an event in poodle. There lies my problem. – Rakshith Ravi Mar 16 '15 at 8:22
  • I see. You could, although it would be unconventional, pass the event to a helper method before it is fired that would do just that, but then you're requiring that your client knows to invoke it this different way instead of the normal way. I cannot think of a way around this limitation. Seems like you have to deal with it if you're using an interface implementation. – computerGuyCJ Mar 16 '15 at 18:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.