Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Subclass of template class with type T specified internally (i.e. invisible)

Discussion in 'Scripting' started by Industrion, Jun 23, 2015.

  1. Industrion

    Industrion

    Joined:
    Nov 21, 2013
    Posts:
    41
    I'd like to create a template class (specifically templating a C# Enum, which means the type constraints are struct and IConvertible) that is subclassed by a non-template class, which itself is then derived, and this twice-derived class specifies the template type of the top-level class internally (i.e. invisibly) - is this recognised/possible (even if it's not wise - that can be a later concern)?

    The class declarations, I'd imagined, would look something like this:

    class MyBaseClass
    class MyTemplateClass<T> : MyBaseClass where T : struct, IConvertible

    And here's the part I'm not sure about. I want this intermediate class to exist, but don't want to specify what T is, until it is subclassed. One of the reasons for this is that I'd like to be able to collectively refer to MyIntermediateClass elsewhere in my code, without specifying what the type is. I don't want to combine MyTemplateClass and MyIntermediateClass because in future I want other classes to subclass MyTemplateClass without going near MyIntermediateClass.

    So I imagined this implementation, if possible, would look something like this:

    class MyIntermediateClass : MyTemplateClass or
    class MyIntermediateClass : MyTemplateClass<T>

    and then
    class MyFinalClass1 : MyIntermediateClass<SomeEnum>
    class MyFinalClass2 : MyIntermediateClass<AnotherEnum>

    In a sense, I feel like MyIntermediateClass is abstract, but I'm still not sure what to do with it.


    In summary, what this is trying to achieve is:
    1. Keep MyTemplateClass and MyIntermediateClass separate.
    2. Avoid having to refer to MyIntermediateClass by always specifying a template type, or at least be able to typedef MyIntermediateClass<struct or IConvertible> to create shorthand for this.
    3. If this is all possible, discover the correct syntax for creating MyFinalClass1 and MyFinalClass2.

    If this even makes sense, let me know if you have any idea or suggestions!

    Thanks
     
  2. blizzy

    blizzy

    Joined:
    Apr 27, 2014
    Posts:
    775
    Please forgive me, but when I see someone building such a construct in their head, I cannot help but to ask, WHY? Why would you need such a complicated structure? I'd really like the person to think hard about whether it's really, absolutely necessary to make things as complicated as that, or if there's not a simpler, more pragmatic approach that does the same.
     
  3. Industrion

    Industrion

    Joined:
    Nov 21, 2013
    Posts:
    41
    It's not that I have no other solution to this problem - I'm just trying something out, and I'm curious about whether I'm missing out on a language feature I didn't know of. I know what you mean, though, but I'm asking a question about whether the language supports a particular arrangement, and I'd then make my judgement on whether it's worth the difficulty - if the language supports something, the difficulty of implementation is likely to be reduced or made more elegant, which is why I investigate these things rather than racing ahead with something that's pragmatic but not exploratory - I want to do both, if I have the time.

    I'll try and give an example scenario for the above question that doesn't reveal the context of what I'm working on. Ignoring the syntax here, since I know some of it's wrong:

    class StateManagingBaseClass<T> where T is an Enum
    class Character : StateManagingBaseClass
    class JumperCharacterClass : Character<JumperStateEnum>
    class ChaserCharacterClass : Character<ChaserStateEnum>

    I need to make references to Character in other places (e.g. a centralised method that accepts a Character argument). It really doesn't matter which Character is being specified, and I have loads of such references, so I don't want to specify the type of these characters every time. So this part of my question could be reduced to a question about templates - can the template types inferred in this kind of usage. I imagine the answer is something along the lines of 'don't template the whole class, only template the methods that require such information'. My implementation could work this way, but if it does, I'd like to still tie a type with each subclass, rather than allowing the invocation of these such methods on any subclasses, allowing mix and match of the type of T, such as myJumperCharacter.SetState(ChaserStateEnum.GetReadyToChase).

    Like I said before, I could combine Character and StateManagingBaseClass to get around some of my issue, but I expect to want to use StateManagingBaseClass for something separate to Character entirely.
     
  4. blizzy

    blizzy

    Joined:
    Apr 27, 2014
    Posts:
    775
    You can't subclass a class and pass a type parameter if that class doesn't take any, such as your Character class. So it really needs to be Character<T> instead. The problem then is, you want to have generic references to Character<T> without knowing what T is. In Java (which I use for many years now at work) this is no problem because you could just use Character<?> for the references. I'm not entirely sure, but I don't think such a construct exists in C#. You could use an interface mixed in somewhere, but I don't think it will solve all the problems with these references.
     
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    What you're trying to do can't be done.

    Character would ineherit all the functionality of StateManagingBaseClass some of which I would imagine uses T (otherwise why is T there in the first place) and T would not be resolved.
     
  6. aerialmantis

    aerialmantis

    Joined:
    Jun 24, 2015
    Posts:
    1
    I am a still a relative beginner when it comes to C#, but hopefully I can help.

    So from what you've described, my understanding is that you want to have a template base class (for different enum classes) that is responsible for specifying the state management, a non template intermediate class that inherits from the base class, that you can refer to generically in a list for example, then a top level non template class that inherits from the intermediate class that you would use to interface with. Also you want to be able to mix and match the enum classes in the base class whilst maintaining the same intermediate class.

    The issue I think is that, as I understand in C# you can't declare a non template class that inherits from a template class without specifying the template parameter. So the intermediate class must specify the template parameter for the base class, it can't be derived from the top level class. Also I don't think it is possible to change the template parameter of the base class at runtime, in order to change the enum type of the base class you would have create a new instance with the new enum type.

    One possible solution I can think of is having the base class encapsulated as a member, rather than inherited. So create an additional non template base class that the template base class derives from. Then have the non template base class be a member of the intermediate class, and then when you want to change the state you can modify the member, and if the state is a base class with a different enum type for it's template parameter than the current base class then you can reconstruct it with the new enum type. Then in the top level class you can simply specify the state you wish the base class to have with a method.

    So this is what it would look like:

    abstract class StateManagerBase
    {
    protected System.Type m_enumType;

    public System.Type GetEnumType()
    {
    return m_enumType;
    }

    public abstract void ManageState();
    }

    class StateManager<T> : StateManagerBase where T : struct, IConvertible
    {
    private T m_state;

    public override void ManageState()
    {
    /* State management code based on enum type and m_state */
    }

    public void SetState(T state)
    {
    m_enumType = typeof(T);

    m_state = state;
    }
    }

    class Character
    {
    public void SetState<T>(T state) where T : struct, IConvertible
    {
    if (m_stateManager.GetType().Equals (typeof(T)))
    {
    StateManager<T> newStateManager = (StateManager<T>)m_stateManager;
    newStateManager.SetState(state);
    }
    else
    {
    StateManager<T> newStateManager = new StateManager<T>();
    newStateManager.SetState(state);
    m_stateManager = newStateManager;
    }
    }

    private StateManagerBase m_stateManager = null;
    }

    enum JumperEnum { A, B }
    enum ChaserEnum { A, B }

    class Jumper : Character
    {
    Jumper()
    {
    SetState(JumperEnum.A);
    }
    }

    class Chaser : Character
    {
    Chaser()
    {
    SetState(ChaserEnum.A);
    }
    }

    class Test
    {
    private Chaser m_chaser;
    private Jumper m_jumper;

    public void Function()
    {
    m_chaser.SetState(JumperEnum.A);
    m_jumper.SetState(ChaserEnum.A);
    }
    }
     
  7. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    971
    This example is breaking the Liskov Substitution Principle.
    Answer this question: is Character a state? I don't think so. Does it have state? Yes, probably. So I think it should be done this way:
    Make the StateManagingBaseClass a member of Character.
    Code (csharp):
    1. abstract class StateManagingClass { }
    2. class Character
    3. {
    4.   StateManagingClass state;
    5.   public Character(StateManagingClass state)
    6.   {
    7.     this.state = state;
    8.   }
    9. }
    Apply the State Pattern and create concrete classes
    Code (csharp):
    1. class JumperState : StateManagingClass { }
    2. class ChaserState : StateManagingClass { }
     
    Last edited: Jun 26, 2015