抽象方法的协变返回类型问题

发布于 2024-08-03 05:34:31 字数 3574 浏览 6 评论 0 原文

我正在尝试结束为期两天的关于抽象方法和返回类型协方差的讨论,我已经发布了两个类似的问题,我永远感谢社区提供的信息,我只需要最后一次推动即可到达终点线。这就是我想要做的:两个抽象类,RecruiterBase 和 CandidateBase,两者都有 RecruiterA 和 CandidateA 的具体实现。 RecruiterBase 有一个抽象方法来让所有招聘的候选人返回 IQueryable。我的 RecruiterA 实现重写了 GetCandidates() 方法以返回 IQueryable。

public abstract class RecruiterBase
{ 
  // Constructors declared here

  public abstract IQueryable<CandidateBase> GetCandidates();
}

public abstract class CandidateBase
{  
  // Constructors declared here
}

和实现:

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

尝试编译会引发编译时错误,因为在我的 RecruitreBase 实现中,GetCandidates() 方法返回 IQueryable 而不是 IQueryable> 。

在无法从上一个问题中获得建议之后(来自抽象的通用返回类型/虚拟方法)工作,我做了很多阅读,并在 SO

如何在 C# 中子类的重写方法中返回子类型?

这最终让我意识到我一直在寻找的是一种实现协方差的方法我的返回类型。我使用 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(); }
}

作为我的解决方案的基础。所以现在我的 RecruiterBase 和 RecruiterA 类看起来像:

public abstract class RecruiterBase
{
  // Constructors declared here

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

  public abstract IQueryable<CandidateBase> GetCandidatesCore();
}

和我的实现...

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

我希望这最终能得到我正在寻找的东西,但我在以下代码中遇到编译时错误,因为 GetCandidates() 无法隐式将 CandidateA 转换为 CandidateBase :

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

所以我添加了一个强制转换:

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

然后一切都会编译,但是当我实际在控制器中调用 GetCandidates() 时,它返回 IQueryable 而不是 IQueryable。所以我又回到了起点。

如果你能成功完成这一切并且可以帮助我,我将寄给你一瓶你最喜欢的 12 瓶啤酒!

I’m trying to wrap up a two day beat down on Abstract methods and return type Covariance, I’ve already posted two similar questions and I am eternally grateful to the community for the info provided, I just need one last push to get to the finish line. Here is what I am trying to do: 2 abstract classes, RecruiterBase and CandidateBase, both have concreate implementations of RecruiterA and CandidateA. RecruiterBase has an abstract method to get all recruited candidates returning IQueryable. My implementation of RecruiterA overrides the GetCandidates() method to return IQueryable.

public abstract class RecruiterBase
{ 
  // Constructors declared here

  public abstract IQueryable<CandidateBase> GetCandidates();
}

public abstract class CandidateBase
{  
  // Constructors declared here
}

and the implementations:

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

Attempting to complile that throw a compile time error because in my implementation of RecruitreBase the GetCandidates() method returns IQueryable<CandidateA> instead of IQueryable<CandidateBase>.

After not being able to get the suggestions from a previous question (Generic return types from abstract/virtual methods) to work, I did a LOT more reading, and came across the following question in SO

How to return subtype in overridden method of subclass in C#?

Which finally made me realize what I had been searching for was a way to implement Covariance for my return type. I used Marc Gravell's snippet...

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

... as the basis for my solution. So now my RecruiterBase and RecruiterA classes look like:

public abstract class RecruiterBase
{
  // Constructors declared here

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

  public abstract IQueryable<CandidateBase> GetCandidatesCore();
}

and my implementation...

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

I was hoping that would finally get me what I was looking for but I got a compile time error in the following code because GetCandidates() cannot implicitly convert CandidateA to CandidateBase:

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

so I added a cast:

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

Everything then compiles but when I actually call GetCandidates() in my controller it returns IQueryable<CandidateBase> instead of IQueryable<CandidateA>. So I am right back where I started.

If you made it all the way through this and you can help me I'll send you a 12 pack of your favorite beer!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不忘初心 2024-08-10 05:34:31

贾斯汀,我有点困惑为什么你需要经历这些麻烦。

如果您的抽象方法的返回类型为 IQueryable 那么这就是您将得到的。我认为这没有问题,因为稍后您仍然可以将其投射回 CandidateACandidateB

那么您到底想实现什么目标?也许我不明白你的问题。

编辑添加:

贾斯汀,这个怎么样?

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

Justin I am a little bit confused why you need to go through all that trouble.

If you abstract method is of return type IQueryable<CandidateBase> then that's what you'll get. I don't see a problem with this, since later on you could still cast it back to CandidateA or CandidateB

So what exactly are you trying to achieve? Maybe I am not understanding you question.

Edit to add:

Justin, what about this?

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();
        }
    }
压抑⊿情绪 2024-08-10 05:34:31

我认为您的意图是好的,但最终结果是您错过了多态代码的要点,并且也失去了价值。

通过抽象类型或接口处理对象的目的是允许您使用任何具体实现,而无需了解任何具体实现细节。我认为您的信念是,通过返回具体类型,您可以生成更高质量的代码,而实际上您开始通过掩盖抽象来否定抽象基类的价值。

一组正确构建的派生类应该只需要很少的需求来通过其具体类型来解决;抽象类应该足以处理所有实现,并且应该处理这些类的绝大多数工作——例外情况应该是少数。

I think your intentions are good, but the net result is that you're missing the point of polymorphic code and also losing the value.

The purpose of working with objects by their abstract type or by their interfaces is to allow you to work with any concrete implementation, without needing to know any concrete implementation details. I think your belief is that by returning concrete types you are producing higher-quality code, when in fact you're beginning to negate the value of the abstract base class by covering up the abstraction.

A properly built set of derived classes should have only very few needs to be addressed by their concrete types; the abstract class should suffice for working with all implementations and should handle the vast majority of work with those classes--the exceptions should be in the minority.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文