How to properly type nested classes with generics in C#

57 Views Asked by At

I'm trying to get the C# equivalent of this Kotlin code:

class Baz<E>(val value: E)

class Foo<T>(val toto: Baz<T>) {

    class Bar<U>(toto: Baz<U>) : Foo<U>(toto) {

        val tata: U = toto.value

    }

}

This works because the U in Bar is the same as the T in Foo, so the same as E in Baz.

Keep in mind that Bar is a nested class, not an inner class, so Bar couldn't have a type for toto if it didn't have the generic U.

I then tried to replicate it in C# like this:

public class Baz<E> {
    public E Value;

    public Baz(E value) {
        Value = value;
    }
}

public class Foo<T> {
    public T Toto;

    public Foo(T toto) {
        Toto = toto;
    }

    public class Bar<U> : Foo<U> {
        public U Tata;

        public Bar(U toto) : base(toto) {
            Tata = toto.Value;
        }
    }
}

However, with this implementation, I get the error Cannot convert source type 'U' to target type 'T'.

Why doesn't it work and how can I fix it?

1

There are 1 best solutions below

0
Dai On BEST ANSWER

However, with this implementation, I get the error Cannot convert source type 'U' to target type 'T'.

Just remove U, because Bar<U> is already generic over T in Foo<T>:

This compiles for me:

public class Foo<T>
{
    public readonly T Toto;

    public Foo( T toto )
    {
        this.Toto = toto;
    }

    public class Bar : Foo<T>
    {
        public readonly T Tata;

        public Bar( T toto )
            : base( toto )
        {
            this.Tata = toto;
        }
    }
}

If you want to allow Bar to have its own type-parameter that's bound to T that's also doable (but of questionable utility, imo):

public class Foo<T>
{
    public readonly T Toto;

    public Foo( T toto )
    {
        this.Toto = toto;
    }

    public class Bar<U> : Foo<T>
        where U : T
    {
        public readonly U Tata;

        public Bar( U toto )
            : base( toto )
        {
            this.Tata = toto;
        }
    }
}

Note that in order to use Bar<U> you need to qualify it as Foo<T>.Bar<U>:

Foo<T> foo = new Foo<T>.Bar<U>( ... );

...so when T == U you end-up with this:

Foo<String> foo = new Foo<String>.Bar<String>( "lolwut" );