Problema com os tipos de retorno covariante de um método abstrato
-
20-09-2019 - |
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!
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.