2012-05-31 25 views
8

tengo el siguiente escenario:Llamar a un método genérico con el correcto tipo derivado

que tengo tres clases, llamémosles A, B y C. Todo lo que tienen en común es que heredan de la misma interfaz, ISomeInterface y que son clases que se asignan a entidades que usan Entity Framework.

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

shell El método se parece a esto

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

Ahora, el problema es con el método de ProcessEntity. Este es un método genérico, que necesita para recuperar la tabla de elementos coincidentes de la base de datos de acuerdo con el tipo o 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ínea var set = repository.Set<T>(); falla porque T es ISomeInterface en este caso, y no el tipo real (A, B o C), por lo que da una excepción que no puede relacionarse con el tipo dado, lo que es comprensible.

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

Respuesta

13

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

public void MyMethod(List<ISomeInterface> entityList) 
{ 
    foreach(var entity in entityList) 
    { 
    dynamic obj = entity; 
    ProcessEntity(obj); 
    } 
} 
+1

Eso hizo el truco. Cambio 'ProcessEntity (obj);' a 'ProcessEntity (obj as dynamic);', y eso funcionó bien. Un uso dinámico que no sabía.Muchas gracias :) –

+0

@ ØyvindKnobloch-Bråthen sí, me gusta mucho esta tipificación en tiempo de ejecución –

2

Bueno, se puede hacer un truco de visitantes similar y utilizar la siguiente solución:

  1. Definir un método Process(EntityProcessor ep) en ISomeInterface
  2. implementarlo en A tan ep.ProcessEntity<A>(this) (y de la misma manera en B y C)
  3. en su bucle, simplemente llame al entity.Process(this).

(los nombres de los métodos no son quizá más limpio, pero usted debe conseguir la idea)

+0

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;)) –

+0

@ Øyvind: bueno, mi solución requiere más trabajo, pero comprueba la existencia del método necesario en tiempo de compilación. Además, podría ser un poco más rápido. – Vlad

+0

Veo su punto, pero al usar la dinámica no tiene un método necesario en absoluto, y preferiría que las clases A, B y C no tengan ningún conocimiento sobre esta funcionalidad también. También significa que no tengo que recordar poner esta funcionalidad adicional para las nuevas clases creadas que se supone que deben seguir el mismo patrón. –

2

Se podría utilizar la reflexión para obtener la definición de método genérico y luego llamarlo, por ejemplo:

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

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

Como alternativa, podría utilizar el Visitor pattern

+0

Sí, ambos deberían funcionar, y estaba pensando en tomar la ruta de reflexión si no obtuve una mejor respuesta aquí. Parece que las soluciones dinámicas son mucho más limpias. Gracias por tu contribución :) –

Cuestiones relacionadas