Alex
Alex

Reputation: 38529

C# - Interfaces / Abstract class - ensure event is raised on method

I have an interface defined as IStore, with two methods:

public interface IStore<TEntity>
{
    TEntity Get(object identifier);
    void Put(TEntity entity);
}

I want an event to be raised on the success of Put (for reference, Put could store a row in a db, or file on the file system etc...)

So, a class implementing Istore for type of Product would look a bit like this:

class MyStore : IStore<Product>
{
    public Product Get(object identifier)
    {
        //whatever
    }

    public void Put(Product entity)
    {
        //Store the product in db
        //RAISE EVENT ON SUCCESS
    }
}

What i'm after, is a way of ensuring that every implementation of IStore raises the event - should i have an abstract class instead, or as well as, the interface?

Upvotes: 8

Views: 1163

Answers (7)

Dan Tao
Dan Tao

Reputation: 128317

Writing an abstract class to wrap a PutInner-like method is absolutely the best way to approach this, as others have already suggested.

I would just add that if you want for any implementation of IStore<TEntity> to always raise an event when Put is called, I would recommend also adding said event to the interface itself:

public interface IStore<TEntity>
{
    event EventHandler<EntityEventArgs<TEntity>> EntityAdded;

    TEntity Get(object identifier);
    void Put(TEntity entity);
}

public EntityEventArgs<TEntity> : EventArgs
{
    public TEntity Entity { get; set; }
}

This doesn't force implementors to do anything; but it does clearly establish an expectation that EntityAdded will be raised on a successful Put.

Upvotes: 0

Arseny
Arseny

Reputation: 7351

 abstract class Store<TEntity>
{
     public abstract TEntity Get(object identifier);
     protected abstract void Put(TEntity entity);
     public void PutBase(TEntity entity)
     {
         Put(entity);
         //Raise event here
     }

}

some note: I did Put as protected so all derived class must implement it but a client code cannot invoke it.

Upvotes: 0

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391406

If you really need to use an interface, then new PostSharp 2 can do aspect inheritance. Unfortunately, to get that feature you need to purchase at least a personal license at $200.

With such an aspect, you would place it on the method declared in your interface, and all implementations of your interface would inherit it.

Upvotes: 0

Thibault Falise
Thibault Falise

Reputation: 5885

Yes, you should use an abstract class instead of an interface.

If you decide to use an abstract class implementing your interface, it will not prevent other developpers from implementing their own version of the interface, which will eventually not raise the event.

That being said, even using an abstract class will not force other developpers to use your method, as they could overwrite it.

I think the best method would be to use a template method :

public abstract class AbstractStore<TEntity>
{
    public TEntity Get(object identifier);
    public sealed void Put(TEntity entity)
    {
        if (DoPut(entity))
        {
            // raise event
        }
    }

    protected abstract bool DoPut(TEntity entity);
}

The real store will have to implement the DoPut method, returning a boolean indicating if the Put operation was successful. The event will be raised from the Put method, which is publicly visible.

Upvotes: 1

Andrew Bezzub
Andrew Bezzub

Reputation: 16032

You need to have abstract class implementing Put method from your interface. Also you can add abstract method like PutImpl, something like this:

public abstract class MyStoreBase : IStore<TEntity>
{
    public abstract TEntity Get(object identifier);

    public abstract void PutImpl(TEntity entity);

    public void Put(TEntity entity)
    {
        // Each inheritor will implement this method.
        PutImpl(entity);

        // But event is fired in base class.
        FireEvent();
    }
}

Upvotes: 2

Andrey
Andrey

Reputation: 60065

my suggestion:

public abstract class Store<TEntity>
{
    public abstract TEntity Get(object identifier);
    public void Put(TEntity entity)
    {
        //Do actions before call
        InternalPut(entity);
        //Raise event or other postprocessing
    }

    protected abstract void InternalPut(TEntity entity);
}

then override InternalPut in your class

Upvotes: 9

Dan McClain
Dan McClain

Reputation: 11920

There is really no way of ensuring that every implementation of IStore raises an event. You could have an abstract class that has the put method, but that still doesn't mean that you could have a put method in a subclass of the abstract class that totally ignores the abstract class's method.

In the end, the best way to encourage the raising of the event is to write the method the developer should use, by way of an abstract class. That way, they have to go out of their way not to use it

Upvotes: 3

Related Questions