samedi 1 janvier 2022

Design issue with matching a generic type on the same type with a parent class as parameter

I'm trying to work out some design issues on code for a rather basic reinforcement learning library I'm working on, but can't seem to figure out where the flaw is. There's this base code which relies on generics:

public abstract class Action {...}

public abstract class State<TAction>
    where TAction : Action
{
    ...

    public abstract TAction[] GetAllowedActions();
    
    public virtual Pair<TAction, State<TAction>> GetPair(TAction action)
    {
        return new Pair<TAction, State<TAction>>(this, action);
    }
}

public class Pair<TAction, TState>
    where TAction : Action
    where TState : State<TAction>
{
    public Pair(TState state, TAction action) {...}

    public virtual IEnumerable EquivalentPairs()
    {
        yield return this;
    }
}

Now I want to build on given code by extending the classes for a certain game I want to train my agent on:

public class GameAction : Action {...}

public class GameState : State<GameAction>
{
    public override Pair<GameAction, State<GameAction>> GetPair(GameAction action)
    {
        return new GamePair(this, action); // CS0029 - GamePair cannot be converted to Pair<GameAction, GameState> 
    }
}

public class GamePair : Pair<GameAction, GameState> // implements additionally required behavior
{
    public GamePair(GameState state, GameAction action) : base(state, action) {...}
}

While Pair wouldn't necessarily have to be extended, it does in this case where certain states being equivalent want to be accounted for, with their corresponding actions adapted accordingly - e.g. in Connect Four, where you additionally could mirror the current game state and the according column being picked to improve training. That's why I'd like additional behavior on this class to be allowed.

This brings me across the problem though, that I can't generally return GamePair with GetPair(...), since it's not recognized as a viable subclass of Pair<GameAction, State<GameAction>>, although GameState is a subclass of State<GameAction>. How to deal with this?

Aucun commentaire:

Enregistrer un commentaire