Home > other >  C# Multi Level Generics with inheritance/polymorphism
C# Multi Level Generics with inheritance/polymorphism

Time:12-27

I've been stumped on this for a while now but wanted to know if it was possible via another approach or I'm just missing a step.

Here are the base classes.

public class StartType0<T1, T2>
    where T1 : StartType1
    where T2 : StartType2
{}

public class StartType1 {}

public class StartType2 {}

public class EndType0 : StartType0<EndType1, EndType2> {}

public class EndType1 : StartType1 {}

public class EndType2 : StartType2 {}

I want to create a holder/container class to hold an array of my EndType0 class which will be used in Unity to draw a generic custom property drawer for my StartType0 class.

Any suggestions or pointers in the right direction would be amazing as I've tried tackling this issue several times over the past few months.

I thought I could try providing every parameter but I can't convert the StartType0 to my EndType0.

public class TypeContainer<T0, T1, T2>
    where T0 : StartType0<T1, T2>
    where T1 : StartType1
    where T2 : StartType2
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<StartType0<EndType1, EndType2>, EndType1, EndType2> container = new TypeContainer<StartType0<EndType1, EndType2>, EndType1, EndType2>();

    public Tester()
    {
        EndType0 instance = (EndType0)container.array[0];
    }
}

I thought I could try providing EndType0 to the StartType0 parameter but I'm guessing it has issues due to not taking polymorphism into account.

public class TypeContainer<T0, T1, T2>
    where T0 : StartType0<T1, T2>
    where T1 : StartType1
    where T2 : StartType2
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<EndType0, EndType1, EndType2> container = new TypeContainer<EndType0, EndType1, EndType2>();

    public Tester()
    {
        EndType0 instance = container.array[0];
    }
}

I would ideally love to approach it something like this way with some yummy polymorphism involved.

public class TypeContainer<T>
    where T : StartType0<StartType1, StartType2>
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<EndType0> container = new TypeContainer<EndType0>();

    public Tester()
    {
        EndType0 instance = container.array[0];
    }
}

-EDIT 1-

After following @JohnathanBarclay's comment I manage to make the following.

public class StartType0<out T1, out T2>
    where T1 : StartType1, IStartType1
    where T2 : StartType2, IStartType2
{}

public interface IStartType1 {}

public class StartType1 : IStartType1 {}

public interface IStartType2 {}

public class StartType2 : IStartType2 {}

public class EndType0 : StartType0<EndType1, EndType2> {}

public class EndType1 : StartType1 {}

public class EndType2 : StartType2 {}

which then allows me to do the following and complete a compile.

public class TypeContainer<T0, T1, T2>
    where T0 : IStartType0<T1, T2>
    where T1 : IStartType1
    where T2 : IStartType2
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<EndType0, EndType1, EndType2> container = new TypeContainer<EndType0, EndType1, EndType2>();

    public Tester()
    {
        EndType0 instance = container.array[0];
    }
}

This has issues being drawn into the unity editor though.

-EDIT 2-

After some play around I found my current ideal setup so far

public interface IStartType0<out T1, out T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{}

public class StartType0<T1, T2>
    IStartType0<T1, T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{}

public interface IStartType1<out T> where T StartType1 {}

public class StartType1 : IStartType1<StartType1> {}

public interface IStartType2<out T> where T StartType2 {}

public class StartType2 : IStartType2<StartType2> {}

public class EndType0 : StartType0<EndType1, EndType2> {}

public class EndType1 : StartType1 {}

public class EndType2 : StartType2 {}

public class TypeContainer<T0, T1, T2>
    where T0 : IStartType0<T1, T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<EndType0, EndType1, EndType2> container = new TypeContainer<EndType0, EndType1, EndType2>();

    public Tester()
    {
        EndType0 instance = container.array[0];
    }
}

Having the 'more or less' guaranteed base types I am able to run my CustomPropertyDrawer over these and generate custom inspectors and even utilise 'Activator' for messing with the generic types.

CodePudding user response:

After some play around with variance in generics I found my current ideal setup so far

public interface IStartType0<out T1, out T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{}

public class StartType0<T1, T2>
    IStartType0<T1, T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{}

public interface IStartType1<out T> where T StartType1 {}

public class StartType1 : IStartType1<StartType1> {}

public interface IStartType2<out T> where T StartType2 {}

public class StartType2 : IStartType2<StartType2> {}

public class EndType0 : StartType0<EndType1, EndType2> {}

public class EndType1 : StartType1 {}

public class EndType2 : StartType2 {}

public class TypeContainer<T0, T1, T2>
    where T0 : IStartType0<T1, T2>
    where T1 : IStartType1<StartType1>
    where T2 : IStartType2<StartType2>
{
    public T0[] array = new T0[1];

    public TypeContainer()
    {
        array[0] = Activator.CreateInstance(typeof(T0));
    }
}

public class Tester
{
    public TypeContainer<EndType0, EndType1, EndType2> container = new TypeContainer<EndType0, EndType1, EndType2>();

    public Tester()
    {
        EndType0 instance = container.array[0];
    }
}

Having the 'more or less' guaranteed base types I am able to run my CustomPropertyDrawer over these and generate custom inspectors and even utilise 'Activator' for messing with the generic types.

  • Related