Вопрос по c#, inheritance, generics – Вызов универсального метода с правильным производным типом

9

У меня есть следующий сценарий:

У меня есть три класса, давайте назовем ихA, B а такжеC, Все, что у них общего, это то, что они наследуются от одного и того же интерфейса,ISomeInterface и что они являются классами, которые сопоставляются с сущностями, использующими Entity Framework.

У меня есть метод, который получил список объектов, который реализует этот интерфейс, но сами объекты будут экземплярамиA, B или жеC.

Оболочка метода выглядит следующим образом

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    ProcessEntity(entity);
  }
}

Теперь проблема сProcessEntity метод. Это универсальный метод, который должен извлечь таблицу соответствующих элементов из базы данных в соответствии с типом или сущностью, поэтому он выглядит следующим образом:

public void ProcessEntity<T>(T entity)
{
  using( var repository = new DbRepository())
  {
    var set = repository.Set<T>();
    ...
  }
}

Проблема в том, что линияvar set = repository.Set<T>(); не удается, потому чтоT являетсяISomeInterface в данном случае, а не фактический тип (A, B или жеC), поэтому он дает исключение, которое не может относиться к данному типу, что понятно.

Поэтому мой вопрос: как я могу вызвать ProcessEntity с фактическим типом объекта внутри списка, а не с типом интерфейса, который они реализуют.

Ваш Ответ

3   ответа
2

Ну, вы можете сделать трюк, похожий на посетителя, и использовать следующий обходной путь:

  1. Define a method Process(EntityProcessor ep) in ISomeInterface
  2. Implement it in A just as ep.ProcessEntity<A>(this) (and the same way in B and C)
  3. Instead of ProcessEntity(entity) in your loop, just call entity.Process(this).

(имена методов, возможно, не самые чистые, но вы должны понять)

@ & # xD8; yvind: хорошо, мое решение требует больше работы, но оно проверяет наличие необходимого метода во время компиляции. Кроме того, это может быть немного быстрее.
Это бы сработало, но ответ Лениберезовского для меня намного чище, поскольку это означает, что мне не нужно больше логизировать в A, B и C (и в действительности существует 20 таких типов, а не 3;) Øyvind Bråthen
@ & # xD8; yvind: использованиеdynamic делает код более компактным, жертвуя некоторой безопасностью типов. Как всегда в разработке программного обеспечения, это компромисс. Для новых классов вы не сможете забыть реализовать необходимый член, как того требует интерфейс.
Полностью согласен там. Это компромисс, как и большинство всего остального. Не уверен, что вы имеете в виду член интерфейса, хотя. Реализация, которую я закончил, не использует члена интерфейса, она просто проверяет, что класс реализует интерфейс, но фактическая реализация интерфейса используется для чего-то другого, а не для этой конкретной функциональности. Øyvind Bråthen
Я понимаю вашу точку зрения, но при использовании динамического метода у вас вообще нет необходимого метода, и я бы предпочел, чтобы у классов A, B и C также не было никаких знаний об этой функциональности. Это также означает, что я не должен помнить, чтобы добавить эту дополнительную функциональность для новых созданных классов, которые должны следовать той же схеме. Øyvind Bråthen
2

Вы можете использовать отражение, чтобы получить определение общего метода, а затем вызвать его, например:

var method = typeof(ClassContainingProcessEntity)
    .GetMethod(ProcessEntity)
    .MakeGenericMethod(entity.GetType);
method.Invoke(this, entity);

Вы можете кэшировать метод по типу, и вы можете скомпилировать его во время выполнения, используя некую фабрику делегатов, если производительность критична.

В качестве альтернативы вы можете использоватьШаблон посетителя

Да, оба должны работать, и я думал о том, чтобы пойти по пути отражения, если я не получу лучшего ответа здесь. Похоже, динамические исправления это намного чище, хотя. Спасибо за ваш вклад :) Øyvind Bråthen
14

Вы можете подать заявкуdynamic ключевое слово при передаче сущности в ProcessEntity. В этом случае фактический тип объекта будет определен во время выполнения.

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    dynamic obj = entity;
    ProcessEntity(obj);
  }
}
@ & # xD8; yvindKnobloch-Br & # xE5; затем да, мне очень нравится эта типизация во время выполнения
Это добилось цели. я меняюProcessEntity(obj); вProcessEntity(obj as dynamic);и это сработало просто отлично. Использование для динамического, о котором я не знал. Большое спасибо :) Øyvind Bråthen

Похожие вопросы