Pregunta Métodos de extensión con interfaz.


Supongamos que tenemos este modelo:

public abstract class AbstractTableReferentielEntity {}
public class EstimationTauxReussite : AbstractTableReferentielEntity { }

Creé un método de extensión para todas las clases que heredan de AbstractTableReferentielEntity.

public static EntityItemViewModel ToEntityItem<T>(this T entity)
    where T : AbstractTableReferentielEntity {}

Pero para un tipo específico de AbstractTableReferentielEntity (como EstimationTauxReussite), me gustaría realizar una acción específica, así que creé un segundo método de extensión.

 public static EntityItemViewModel ToEntityItem(this EstimationTauxReussite entity) {}

Todos los métodos de extensiones se declaran en el mismo espacio de nombres.

Después de eso, recupero algunos datos de una base de datos con Entity Framework:

protected List<EntityItemViewModel> GetAllActifEntityItem<T>()
    where T : AbstractTableReferentielEntity
{
    return Context
        .Set<T>()
        .Where(item => item.IsActif)
        .Select(item => item.ToEntityItem())
        .ToList();
}

Compila.

Cuando T en tiempo de ejecución es un tipo EstimationTauxReussite, entra en el método incorrecto ToEntityItem cuando llamo Select(item => item.ToEntityItem()). No entra en el método de extensión más específico. Algunas ideas ?


6
2017-07-22 09:54


origen


Respuestas:


Esto se debe a que los métodos de extensión son solo azúcar sintáctica para métodos estáticos. El método para llamar se resuelve en tiempo de compilación en función del tipo de argumento en tiempo de compilación, no hay un despacho virtual involucrado.

En tus GetAllActifEntityItem método, el compilador solo sabe que T es un AbstractTableReferentielEntity, así se resuelve el ToEntityItem Método basado en eso. El hecho de que en realidad será llamado con EstimationTauxReussite para T es irrelevante.

Una posible solución sería hacer ToEntityItem un método de miembro virtual de AbstractTableReferentielEntityy anularlo en EstimationTauxReussite. De esta manera, el envío virtual ocurrirá como se espera y se llamará al método correcto.


1
2017-07-22 10:13



La razón es que los métodos de extensión son "azúcar sintáctico", es decir, son un truco de compilación. Tu linea:

.Select(item => item.ToEntityItem())

es efectivamente convertido por el compilador a:

.Select(item => StaticClassWithExtensionMethod.ToEntityItem(item))

y luego se convirtió en IL. Esto significa que el tipo de item tiene que determinarse en tiempo de compilación, no en tiempo de ejecución. Entonces el AbstractTableReferentielEntity Se usa la versión del método de extensión, ya que es la que coincide con el tipo en el momento de la compilación.


3
2017-07-22 10:10



Si tengo acceso a las fuentes de AbstractTableReferentielEntity y EstimationTauxReussite  clases los rehacía de la siguiente manera

  1. Agregue el método virtual ToEntityItem a la clase AbstractTableReferentielEntity
  2. Anularlo en la clase EstimationTauxReussite
  3. Eliminar ambos métodos de extensión

Ahora Select(item => item.ToEntityItem()) El método de selección debería depender del objeto de entrada


1
2017-07-22 10:09