Brian Gradin Brian Gradin - 2 months ago 9
C# Question

Why can't I implicitly convert a class to its generic base class?

public abstract class Base<T>
{
protected abstract InnerFooBase<InnerBarBase<T>> GetInnerFoo();

protected abstract class InnerFooBase<TFoo> where TFoo : InnerBarBase<T>
{
}

protected abstract class InnerBarBase<TBar>
{
}
}

public class Implementation : Base<string>
{
protected override InnerFooBase<InnerBarBase<string>> GetInnerFoo()
{
return new InnerFoo(); // Error
}

class InnerFoo : InnerFooBase<InnerBar>
{
}

class InnerBar : InnerBarBase<string>
{
}
}


Fiddle

Line 18 gives the error "Cannot implicitly convert type
Implementation.InnerFoo
to
Base<string>.InnerFooBase<Base<string>.InnerBarBase<string>>
"

I can fix the error by having
InnerFoo
derive from
InnerBarBase<string>
rather than
InnerBar
, but I do not want to do so. I need the code to enforce the relationship beetween
InnerFoo
and
InnerBar
.

InnerFoo
derives from
InnerFooBase<InnerBar>
, and
InnerBar
derives from
InnerBarBase<string>
. If
GetInnerFoo
is looking for
InnerFooBase<InnerBarBase<string>>
, why can't the types be converted to their bases automatically?

Answer

To illustrate why this fails I am going to rename your classes as follows:

InnerBarBase<T> -> Cage<T>
string -> Fish
InnerBar -> GoldfishBowl
InnerFooBase -> Zoo
InnerFoo -> MiniAquarium

And I'm going to simplify your scenario to use assignment rather than virtual overriding. The problem is more easily seen with assignment.

All right. Let's set up the types.

class Animal {}
class Fish : Animal {}
class Cage<TAnimal> where TAnimal : Animal { }

A cage is a thing that can hold animals.

class GoldfishBowl : Cage<Fish> { }

A goldfish bowl is a cage that can hold fish.

class B<TAnimal> where TAnimal : Animal
{
    public class Zoo<TCage> where TCage : Cage<TAnimal>
    { 
        public void Add(TCage cage) {}
    }
}

A B<TAnimal>.Zoo<TCage> is a collection of cages that can hold the given kind of animal. Note that I have created an "Add" method not present in your original program.

class MiniAquarium : B<Fish>.Zoo<GoldfishBowl> { }

A miniature aquarium is a zoo that contains only goldfish bowls.

And now the question is: can I use a mini aquarium in a context where I want a zoo that contains fish cages? No! Here's why:

B<Fish>.Zoo<Cage<Fish>> zoo = new MiniAquarium();

Let's suppose that is legal. It is not legal. But let's suppose it is. What goes wrong? I create a second kind of fish cage:

class SharkTank : Cage<Fish> { }

And now it is perfectly legal for me to say:

    zoo.Add(new SharkTank());

And now we have a mini aquarium -- which by definition contains only goldfish bowls -- and it has a shark tank in it.

The only place where the compiler can produce the error is on the conversion from MiniAquarium to zoo of fish cages.

You can only do this sort of conversion -- a covariant conversion -- if the types are interfaces, the type parameters are reference types, and the interface has no "add" style methods, and the varying parameter is marked "out".