C# Доступ к атрибутам объекта после того, как он убедился, что он реализует интерфейс
Мне нужно, чтобы определенные атрибуты моих объектов POCO были обновлены при определенных действиях.
Я определил следующие интерфейсы:
public interface IEntityPOCO
{
Guid Id { get; set; }
}
public interface IHasLastChange : IEntityPOCO
{
DateTime LastChange { get; set; }
}
Вот пример метода действия:
public void SomeStuff<T>(T entity) where T : class, IEntityPOCO
{
entity.Id = Guid.NewGuid(); // Works like a charm.
if (entity is IHasLastChange)
{
entity.LastChange = DateTime.Now; // Doesn't work. D'oh.
(IHasLastChange)entity.LastChange = DateTime.Now; // Doesn't work either.
}
}
- Поскольку POCO может реализовать довольно много различных свойств (все с соответствующими интерфейсами), вопрос о разных сигнатурах функций в значительной степени исключен.
- Я бы предпочел не использовать отражение по соображениям производительности.
Есть ли реальный способ избавить меня от страданий?
3 ответа
Тебе нужно больше ()
в кастинге:
// (IHasLastChange)entity.LastChange = DateTime.Now;
((IHasLastChange)entity).LastChange = DateTime.Now;
Так как есть довольно много разных свойств
Тогда он быстро заплатит, чтобы использовать временную переменную:
if (entity is IHasLastChange)
{
lastChange = (IHasLastChange) entity;
lastChange.LastChange = DateTime.Now;
lastChange. ...
}
Обычным способом сделать это было бы разыграть сущность с as
и проверьте, если это null
, как это:
var lastChangedEntity = entity as IHasLastChange;
if (lastChangedEntity != null)
{
lastChangedEntity.LastChange = DateTime.Now;
}
Но это приводит к неприятному запаху кода при увеличении количества различных интерфейсов.
Если это так, рассмотрите возможность использования шаблона стратегии для его обработки. Таким образом, вы будете придерживаться принципа Open/Closed (OCP) и сделаете ваше приложение более обслуживаемым.
РЕДАКТИРОВАТЬ: Пример шаблона стратегии будет:
public void SomeStuff<T>(T entity) where T : class, IEntityPOCO
{
entity.Id = Guid.NewGuid(); // Works like a charm.
// _processors is a List<IProcessEntities>
var processors = _processors.Where(p => p.CanProcess(entity));
foreach (var processor in processors)
{
processor.Process(entity);
}
}
public interface IProcessEntities
{
bool CanProcess<T>(T entity) where T : class, IEntityPOCO;
void Process<T>(T entity) where T : class, IEntityPOCO;
}
public class LastChangedProcessor : IProcessEntities
{
public bool CanProcess<T>(T entity) where T : class, IEntityPOCO
{
return typeof(IHasLastChange).IsAssignableFrom(typeof(T));
}
public void Process<T>(T entity) where T : class, IEntityPOCO
{
var lastChangeEntity = entity as IHasLastChange;
if (lastChangeEntity != null)
{
lastChangeEntity.LastChange = DateTime.Now;
}
}
}
Вам нужно сыграть.
var hasLastChange = entity as IHasLastChange;
hasLastChange.LastChange = DateTime.Now; //It works!
Или же
((IHasLastChange)entity).LastChange = DateTime.Now; //It also works!