1

So I have this abstract class. Which has a list of abstract types

public abstract class BaseClass
{

    public abstract List<A> History { get; set; }

and the inheritor class.

public class ConcreteClass : BaseClass
{
    public override List<B> History { get; set; }

My question is simple. Why does this return an error. A is an abstract class that b inherits. So why can't I have a concrete property override an abstract property?

Note:Due to other issues I cannot use generics with the base class. Some clarity on the issue. I am using mvc and due to some problems with dynamic types I cannot say BaseClass<t> And in some other areas methods that call a method called GetHistory() need it to return the concrete list not the abstract list. So I am stuck between a rock and a hard place.

trinityalps
  • 397
  • 4
  • 19
  • Because If I do `BaseClass c = new ConcreteClass();` then I must be able to add instances of *any* subclass of `A` to `c.History`. – Blorgbeard Aug 25 '17 at 22:27
  • OH right. Well is there any way around this? – trinityalps Aug 25 '17 at 22:28
  • 1
    Well, that depends on what you're trying to do. Can you take a step back and describe what you're trying solve with this? – Blorgbeard Aug 25 '17 at 22:28
  • @Blorgbeard I added some more info for the question. – trinityalps Aug 25 '17 at 22:32
  • 1
    Do `A` and `B` both implement the same interface? If they are used in this similar manner, then there is probably some similarity. In that case, you could just make the list be of type IHistory or whatever. `List History { get; set;}` – user1304444 Aug 25 '17 at 22:32
  • 1
    @trinityalps: You haven't really added to the question what you're actually trying to do. Or elaborated at all on what you mean by "problems with dynamic types in MVC". – David Aug 25 '17 at 22:34
  • 1
    `Why does this return an error.` In instances like this, it is important that you share the compiler error with us. It makes helping you much easier. – mjwills Aug 25 '17 at 22:58

2 Answers2

6

Why does this return an error. A is an abstract class that B inherits.

Once again. This question is asked pretty much every day.

abstract class Fruit 
{
  public virtual List<Fruit> M() { return new List<Fruit>(); }
}
class Apple : Fruit
{
  public override List<Apple> M() { return new List<Apple>(); }
}

Suppose that was legal. What goes wrong?

class Banana : Fruit { }

...

Fruit f = new Apple(); // Legal
List<Fruit> bowl = f.M(); // calls Apple.M, returns a list of apples.
bowl.Add(new Banana());

And now there is a banana in a bowl that may only contain apples.

That's why this has to be illegal. Do a search for covariance and contravariance to learn when this kind of conversion is legal in C# and when it is illegal. Briefly, it is legal only when the compiler can prove that it is safe, and when the type arguments are reference types.

Now, what about this?

abstract class Fruit 
{
  public virtual Fruit N() { return new Banana(); }
}
class Apple : Fruit {
  public override Apple N() { return new Apple(); }
}
...
Fruit f1 = new Apple();
Fruit f2 = f1.N(); // No problem, an apple is a fruit.

This doesn't wreck the type system; this would be safe but it is still illegal. It's illegal simply because virtual return type covariance has never been implemented in C#. It is implemented in C++.

It hasn't been implemented because simply it's never been a high priority for the C# team. People have been asking for it for over a decade, but it's just not a great feature. Maybe it will get done eventually; if you feel strongly about it, join the forum on github and advocate for it.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
0

One approach you could consider is:

public class ConcreteClass : BaseClass<ListClass>
{
    public override List<ListClass> History { get; set; }
}

public abstract class BaseClass<T> where T : BaseListClass
{
    public abstract List<T> History { get; set; }
}

public abstract class BaseListClass
{
}

public class ListClass : BaseListClass
{
}

By making the concrete class specify the exact generic type it wants to use (re: the base type) then it may suit your purposes.

mjwills
  • 23,389
  • 6
  • 40
  • 63