-1

I don't understand why the below code doesn't work in c# (.NET Core 1.1).

public interface IChild
{
}

public interface IParent
{
    IEnumerable<IChild> Children { get; set; }
}

public abstract class ParentBase<TChild> : IParent
    where TChild : IChild
{
    public IEnumerable<TChild> Children {get; set; }
}

The error message is

Error CS0738 'ParentBase< TChild>' does not implement interface member 'IParent.Children'. 'ParentBase< TChild>.Children' cannot implement 'IParent.Children' because it does not have the matching return type of 'IEnumerable< IChild>'.

The error message says the IEnumerable< TChild> is not of type IEnumerable< IChild> while I constrain TChild to IChild.

I found two 'easy' ways out of this, but both require rather bad changes in the client code. I load an entire object tree of these types using ConfigurationSection.Bind() in the client code.

Unwanted solution 1 : Make the parent interface generic

public interface IChild
{
}

public interface IParent<TChild>
    where TChild : IChild
{
    IEnumerable<TChild> Children { get; set; }
}

public abstract class ParentBas<TChild> : IParent<TChild>
    where TChild : IChild
{
    public IEnumerable<TChild> Children {get; set; }
}

Unwanted solution 2 :

public interface IChild
{
}

public interface IParent
{
    IEnumerable<IChild> Children { get; set; }
}

public abstract class ParentBas<TChild> : IParent
    where TChild : IChild
{
    public IEnumerable<IChild> Children {get; set; }
}
  • 4
    Why you using generic `ParentBas`? You don't need it - `IEnumerable Children` already accept all implementations of `IChild`? – Fabio May 01 '17 at 10:29
  • *"I don't understand why the below code doesn't work in c#"* - because it would lead to an inconsistency between the interface and the concrete class, for example allowing to add a dog child to a cat parent through the interface – Ofir Winegarten May 01 '17 at 10:57
  • Well, ParentBas is missspelled, it should be ParentBase. The purpose of having an abstract Parent base class is to implement common functionality for the Parent in there. I have remove that common code for clarity. – Stefaan Vandevelde May 01 '17 at 13:52
  • Your question is exactly like all the other questions already on Stack Overflow confusing inheritance of types with inheritance of type parameters. See marked duplicate for a good explanation. – Peter Duniho May 02 '17 at 03:06

1 Answers1

0

The reason this doesn't work is pretty is simple:

public interface Animal { }

public interface ICage
{
   IEnumerable<IAnimal> Animals { get; set; }
}

public class Cage<TAnimal> where Animal: Animal
{
    public IEnumerable<TAnimal> { get; set; } //Illegal implementation of ICage
}

Ok, this is basically the same set up without some unnecessary cruft. Now see what happens if what you are trying were a legal implementation of the interface:

var dogCage = new Cage<Dog>();
dogCage.Animals = new[] { cat }; //Illegal! Why?!?

Now, although that seems reasonable, it is a flagrant violation of the ICage contract. This should be and in fact is legal:

ICage dogCage = new Cage<Dog>();
dogCage.Animals = new[] { cat }; //Legal!

So, you can obviously see why your code does not satisfy the contract of IParent at all.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Sorry, not clear to me at all. If dogCage is of type Cage then adding cats to the Animals list should be illegal in both examples. Still don't get why my declarations of IParent, IChild and ParentBase are illegal. – Stefaan Vandevelde May 01 '17 at 15:17
  • @StefaanVandevelde well because the interface `ICage` states it admits *any* `IEnumerable`, that includes `IEnumerable`. With your proposal `Cage` only admits `IEnumerable`s. That violates the contract of `ICage`. – InBetween May 01 '17 at 15:22
  • @StefaanVandevelde and that's the point precisely, your interface doesn't disallow anything, any `IAnimal` is valid, no matter what the generic type of the implementing class is. Why should the interface worry about `TAnimal`? It knows nothing about it... – InBetween May 01 '17 at 15:25
  • OK, Yes, that makes sense – Stefaan Vandevelde May 01 '17 at 15:57