Search code examples
c#genericsinner-classes

How to properly type nested classes with generics in C#


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?


Solution

  • 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" );