Pregunta sobre c#, generics, inheritance – Llamando a un método genérico con el tipo derivado correcto

9

Tengo el siguiente escenario:

Tengo tres clases, llamémoslasA, B yC. Todo lo que tienen en común es que heredan de la misma interfaz,ISomeInterface y que son clases que se asignan a entidades utilizando Entity Framework.

Tengo un método que recibió una lista de objetos que implementa esta interfaz, pero los objetos en sí mismos serán instancias deA, B oC.

El método shell se ve así

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

Ahora, el problema es con elProcessEntity método. Este es un método genérico, que necesita recuperar la tabla de elementos coincidentes de la base de datos según el tipo o la entidad, por lo que se ve así:

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

El problema es que la líneavar set = repository.Set<T>(); falla porqueT esISomeInterface en este caso, y no el tipo real (A, B oC), por lo que da una excepción que no puede relacionarse con el tipo dado, lo cual es comprensible.

Entonces mi pregunta es: ¿Cómo puedo llamar a ProcessEntity con el tipo real del objeto dentro de la lista, y no con el tipo de interfaz que implementan?

Tu respuesta

3   la respuesta
2

Bueno, puedes hacer un truco similar a un visitante y usar la siguiente solución:

Define un métodoProcess(EntityProcessor ep) enISomeInterfaceImplementalo enA Tal comoep.ProcessEntity<A>(this) (y de la misma manera enB yC)En lugar deProcessEntity(entity) en tu bucle, solo llamaentity.Process(this).

(los nombres de los métodos quizás no sean los más limpios, pero debería tener una idea)

@ Øyvind: en mi ejemplo, se agrega una nueva función aISomeInterface (que es de hecho un tipo de apoyo de patrón de visitante). De esta manera cadaentity Tiene que implementarlo, como implementa.ISomeInterface. Vlad
Sí, y eso es lo que estoy evitando al usar dinámica. Por lo tanto, la compensación en este caso es la pérdida de algún tipo de seguridad (y estoy de acuerdo con esto en este caso, ya que es solo dentro de ese método) para mantener la clase de clase más limpia al no forzar ningún método de interfaz en ellos que debe ser implementado por todos ellos. Øyvind Bråthen
Totalmente de acuerdo allí. Es una compensación como la mayoría de todo lo demás. No estoy seguro de a qué se refiere el miembro de la interfaz. La implementación con la que terminé no usa ningún miembro de la interfaz, solo verifica que la clase implementa la interfaz, pero la implementación de la interfaz real se usa para otra cosa, y no para esta funcionalidad específica. Øyvind Bråthen
Esto funcionaría, pero la respuesta de lazyberezovsky es mucho más limpia para mí, ya que eso significa que no tengo que poner más lógica en A, B y C (y hay aproximadamente 20 de estos tipos, no 3;)) Øyvind Bråthen
14

Puedes aplicardynamic palabra clave al pasar entidad a ProcessEntity. En este caso, el tipo real de entidad se determinará en el tiempo de ejecución.

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    dynamic obj = entity;
    ProcessEntity(obj);
  }
}
Eso hizo el truco. Yo cambioProcessEntity(obj); aProcessEntity(obj as dynamic);, y eso funcionó bien. Un uso para la dinámica que no conocía. Muchas gracias :) Øyvind Bråthen
@ ØyvindKnobloch-Bråthen sí, me gusta mucho esta tipificación en tiempo de ejecución Sergey Berezovskiy
2

Podría usar la reflexión para obtener la definición del método genérico y luego llamarla, por ejemplo:

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

Puede almacenar en caché el método por tipo y compilarlo en tiempo de ejecución utilizando algún tipo de fábrica de delegados si el rendimiento es crítico.

Alternativamente, usted podría usar elPatrón de visitante

Sí, ambos deberían funcionar, y estaba pensando en seguir la ruta de la reflexión si no tengo una respuesta mejor aquí. Aunque parece que las correcciones dinámicas son mucho más limpias. Gracias por tu contribución :) Øyvind Bråthen

Preguntas relacionadas