Pregunta

Estoy tratando de concluir una revisión de dos días sobre los métodos abstractos y la covarianza del tipo de retorno. Ya publiqué dos preguntas similares y estoy eternamente agradecido a la comunidad por la información proporcionada. Solo necesito un último empujón para llegar. la linea final.Esto es lo que estoy tratando de hacer:2 clases abstractas, RecruiterBase y CandidateBase, ambas tienen implementaciones concretas de RecruiterA y CandidateA.RecruiterBase tiene un método abstracto para hacer que todos los candidatos reclutados regresen a IQueryable.Mi implementación de RecruiterA anula el método GetCandidates() para devolver IQueryable.

public abstract class RecruiterBase
{ 
  // Constructors declared here

  public abstract IQueryable<CandidateBase> GetCandidates();
}

public abstract class CandidateBase
{  
  // Constructors declared here
}

y las implementaciones:

public class CandidateA : CandidateBase
{
  // Constructors declared here
}

public class RecruiterA : RecruiterBase
{
  // Constructors declared here

  // ----HERE IS WHERE I AM BREAKING DOWN----
  public override IQueryable<CandidateA> GetCandidates()
  {
     return from c in db.Candidates
            where c.RecruiterId == this.RecruiterId
            select new CandidateA
            {
              CandidateId = c.CandidateId,
              CandidateName = c.CandidateName,
              RecruiterId = c.RecruiterId
            };
  }
}

Al intentar compilar, se produce un error de tiempo de compilación porque en mi implementación de RecruitreBase el método GetCandidates() devuelve IQueryable<CandidateA> en lugar de IQueryable<CandidateBase>.

Después de no poder obtener las sugerencias de una pregunta anterior (Tipos de retorno genéricos de métodos abstractos/virtuales) para trabajar, leí MUCHO más y encontré la siguiente pregunta en SO

¿Cómo devolver el subtipo en el método anulado de la subclase en C#?

Lo que finalmente me hizo darme cuenta de que lo que había estado buscando era una forma de implementar la covarianza para mi tipo de devolución.Utilicé el fragmento de Marc Gravell...

abstract class BaseClass
{
    public BaseReturnType PolymorphicMethod()
    { return PolymorphicMethodCore();}

    protected abstract BaseReturnType PolymorphicMethodCore();
}

class DerivedClass : BaseClass
{
    protected override BaseReturnType PolymorphicMethodCore()
    { return PolymorphicMethod(); }

    public new DerivedReturnType PolymorphicMethod()
    { return new DerivedReturnType(); }
}

...como base para mi solución.Ahora mis clases RecruiterBase y RecruiterA se ven así:

public abstract class RecruiterBase
{
  // Constructors declared here

  public IQueryable<CandidateBase> GetCandidates()
  {
     return GetCandidatesCore();
  }

  public abstract IQueryable<CandidateBase> GetCandidatesCore();
}

y mi implementación...

public class RecruiterA : RecruiterBase
{
  // Constructors

  protected override IQueryable<CandidateBase> GetCandidatesCore()
  {
    return GetCandidates();
  }

  public new IQueryable<CandidateA> GetCandidates()
  {
    return from candidates in db.Candidates
           select new CandidateA
           {
             CandidateId = candidates.CandidateId,
             RecruiterId = candidates.RecruiterId
           };
  }
}

Esperaba que eso finalmente me diera lo que estaba buscando, pero recibí un error de tiempo de compilación en el siguiente código porque GetCandidates() no puede convertir implícitamente CandidateA a CandidateBase:

  protected override IQueryable<CandidateBase> GetCandidatesCore()
  {
    return GetCandidates();
  }

así que agregué un elenco:

  protected override IQueryable<CandidateBase> GetCandidatesCore()
  {
    return ((IQueryable<CandidateBase>)GetCandidates());
  }

Luego todo se compila, pero cuando llamo a GetCandidates() en mi controlador, devuelve IQueryable<CandidateBase> en lugar de IQueryable<CandidateA>.Así que estoy de regreso donde comencé.

Si lograste llegar hasta el final y puedes ayudarme, ¡te enviaré un paquete de 12 de tu cerveza favorita!

¿Fue útil?

Solución

Justin Estoy un poco confundido por qué tiene que pasar por todo ese problema.

Si el método abstracto es del tipo de retorno IQueryable<CandidateBase> entonces eso es lo que se obtiene. No veo un problema con esto, ya que más tarde todavía se podía echarlo de nuevo a candidatea o CandidateB

Entonces, ¿qué estás tratando de lograr? Tal vez no soy la comprensión de que se trate.

Editar para añadir:

Justin, ¿qué pasa con esto?

public abstract class RecruiterBase<T>
    {
        // Constructors declared here

        public abstract IQueryable<CandidateBase> GetCandidates();
    }

    public abstract class CandidateBase
    {
        // Constructors declared here
    }


    public class CandidateA : CandidateBase
    {

    }

    public class RecruiterA : RecruiterBase<RecruiterA>
    {
        // Constructors declared here

        // ----HERE IS WHERE I AM BREAKING DOWN----
        public override IQueryable<CandidateBase> GetCandidates()
        {
            return db.Candidates.Where(cand => cand.RecruiterId == this.RecruiterId)
                         .Select(x => new CandidateA
                                          {
                                             CandidateId = c.CandidateId,
                                             CandidateName = c.CandidateName,
                                             RecruiterId = c.RecruiterId
                                           })
                         .Cast<CandidateBase>()
                         .AsQueryable();
        }
    }

Otros consejos

Creo que sus intenciones son buenas, pero el resultado neto es que se está perdiendo el punto de código polimórfico y también perder el valor.

El propósito de trabajar con objetos de su tipo abstracto o por sus interfaces, permitirá trabajar con ningún aplicación concreta, sin necesidad de conocer ningún concretos detalles de implementación . Creo que su creencia es que mediante la devolución de los tipos de hormigón que se está produciendo código de mayor calidad, cuando en realidad estás empezando a negar el valor de la clase base abstracta por encubrir la abstracción.

Un conjunto adecuadamente construida de clases derivadas deben tener muy pocos debe ser abordado por sus tipos concretos; la clase abstracta debería ser suficiente para trabajar con todas las implementaciones y debe manejar la gran mayoría del trabajo con esas clases -. las excepciones deben estar en la minoría

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top