Pergunta

Estou tentando encerrar uma derrota de dois dias nos métodos abstratos e a covariância do tipo de retorno, já publiquei duas perguntas semelhantes e sou eternamente grato à comunidade pelas informações fornecidas, só preciso de um último empurrão para chegar a linha de chegada. Aqui está o que estou tentando fazer: 2 classes abstratas, RecruiterBase e CandidateBase, ambos têm implementações concretas de recruta e candidatea. O RecruiterBase possui um método abstrato para que todos os candidatos recrutados retornem à IQueryable. Minha implementação do RecruitRera substitui o método getCandidates () para retornar iQueryable.

public abstract class RecruiterBase
{ 
  // Constructors declared here

  public abstract IQueryable<CandidateBase> GetCandidates();
}

public abstract class CandidateBase
{  
  // Constructors declared here
}

e as implementações:

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
            };
  }
}

Tentando complicar que lançam um erro de tempo de compilação porque, na minha implementação do RecruitRebase, o método getCandidates () retorna IQueryable<CandidateA> ao invés de IQueryable<CandidateBase>.

Depois de não conseguir obter as sugestões de uma pergunta anterior (Tipos de retorno genéricos de métodos abstratos/virtuais) Para trabalhar, eu fiz muito mais lendo e me deparei com a seguinte pergunta em So

Como retornar o subtipo no método de subclasse substituído em C#?

O que finalmente me fez perceber o que eu estava procurando era uma maneira de implementar covariância para o meu tipo de retorno. Eu usei o trecho de Marc Granell ...

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 minha solução. Então agora minhas classes RecruiterBase e RecruitERera parecem:

public abstract class RecruiterBase
{
  // Constructors declared here

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

  public abstract IQueryable<CandidateBase> GetCandidatesCore();
}

E minha implementação ...

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
           };
  }
}

Eu esperava que finalmente me desse o que estava procurando, mas recebi um erro de tempo de compilação no código a seguir, porque getCandidates () não pode converter implicitamente candidatea em candidatebase:

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

Então eu adicionei um elenco:

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

Tudo então compila, mas quando eu realmente chamo getCandidates () no meu controlador, ele retorna IQueryable<CandidateBase> ao invés de IQueryable<CandidateA>. Então, estou de volta onde comecei.

Se você fez isso até isso e puder me ajudar, enviarei um pacote de 12 da sua cerveja favorita!

Foi útil?

Solução

Justin Estou um pouco confuso por que você precisa passar por todo esse problema.

Se você abstrair o método é do tipo de retorno IQueryable<CandidateBase> Então é isso que você receberá. Não vejo um problema com isso, pois mais tarde você ainda pode lançá -lo de volta para Candidatea ou Candidatob

Então, o que exatamente você está tentando alcançar? Talvez eu não esteja entendendo sua pergunta.

Editar para adicionar:

Justin, e quanto a isso?

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();
        }
    }

Outras dicas

Eu acho que suas intenções são boas, mas o resultado líquido é que você está perdendo o ponto do código polimórfico e também perde o valor.

O objetivo de trabalhar com objetos por seu tipo abstrato ou por suas interfaces é permitir que você trabalhe com algum implementação concreta, sem precisar saber algum Detalhes da implementação concreta. Eu acho que sua crença é que, ao retornar tipos de concreto, você está produzindo código de alta qualidade, quando na verdade você está começando a negar o valor da classe base abstrata, cobrindo a abstração.

Um conjunto de classes derivadas adequadamente construído deve ter muito poucas necessidades para ser abordada por seus tipos de concreto; A classe abstrata deve ser suficiente para trabalhar com todas as implementações e deve lidar com a grande maioria do trabalho com essas classes-as exceções devem estar em minoria.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top