Мне определили интерфейс как IStore с двумя методами:
public interface IStore<TEntity>
{
TEntity Get(object identifier);
void Put(TEntity entity);
}
Я хочу, чтобы событие было повышено на успехе Помещенных (для ссылки, Помещенный мог сохранить строку в дб или файл в файловой системе и т.д....),
Так, реализация класса Istore для типа продукта немного походила бы на это:
class MyStore : IStore<Product>
{
public Product Get(object identifier)
{
//whatever
}
public void Put(Product entity)
{
//Store the product in db
//RAISE EVENT ON SUCCESS
}
}
То, что я после, является способом гарантировать, что каждая реализация IStore генерирует событие - у меня должен быть абстрактный класс вместо этого, или а также, интерфейс?
мое предложение:
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);
}
затем переопределите InternalPut
в вашем классе
На самом деле нет способа гарантировать, что каждая реализация IStore вызывает событие.У вас может быть абстрактный класс с методом put, но это еще не означает, что у вас может быть метод put в подклассе абстрактного класса, который полностью игнорирует метод абстрактного класса.
В конце концов, лучший способ поощрить инициирование события - написать метод, который разработчик должен использовать, посредством абстрактного класса. Таким образом, они должны стараться изо всех сил , а не , чтобы использовать его
Вам нужно иметь абстрактный класс, реализующий метод Put из вашего интерфейса. Также вы можете добавить абстрактный метод типа PutImpl, что-то вроде этого:
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();
}
}
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
}
}
некоторое примечание: я сделал Put как protected, так что все производные классы должны реализовать его, но клиентский код не может вызвать его.
Написание абстрактного класса для обертывания PutInner
-подобного метода - лучший способ приблизиться к этому, как уже предлагали другие.
Я бы просто добавил, что если вы хотите, чтобы любая реализация IStore
в всегда вызывала событие, когда вызывается Put
, я бы порекомендовал также добавление указанного события к самому интерфейсу:
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; }
}
Это не заставляет разработчиков что-либо делать; но он четко устанавливает ожидание того, что EntityAdded
будет вызван при успешном Put
.
Да, вам следует использовать абстрактный класс вместо интерфейса.
Если вы решите использовать абстрактный класс, реализующий ваш интерфейс, это не помешает другим разработчикам реализовать свою собственную версию интерфейса, которая в конечном итоге не будет вызывать событие.
При этом даже использование абстрактного класса не заставит других разработчиков использовать ваш метод, так как они могут его переписать.
Я думаю, что лучшим методом будет использование шаблонного метода :
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);
}
Реальный магазин должен будет реализовать метод DoPut, возвращающий булево значение, указывающее, была ли операция Put успешной. Событие будет вызвано из метода Put, который является общедоступным.
Если вам действительно нужно использовать интерфейс, тогда новый PostSharp 2 может выполнять наследование аспектов. К сожалению, чтобы получить эту функцию, вам нужно приобрести как минимум персональную лицензию за 200 долларов.
С таким аспектом вы поместите его в метод, объявленный в вашем интерфейсе, и все реализации вашего интерфейса унаследуют его.