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".
Moving back into your domain: we cannot use an InnerFoo
in a context where a InnerFooBase<InnerBarBase<string>>
is needed. An InnerFoo
is an InnerFooBase
that consumes only InnerBar
s, but we need an InnerFooBase
that can consume any InnerBarBase<string>
. There might be derived classes of InnerBarBase<string>
out there in the world that are not InnerBar
. And so the type system refuses to let you do this.
Given how confusing this is, you might consider taking a step back and asking if your arrangement of generic types and nested types is simply too complex for future programmers to understand. There's no need to try to capture all possible restrictions on program behaviour in the type system. And as we've just seen, when you try to, sometimes the type system enforces restrictions you didn't intend to express.