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!
Другие вопросы по тегам