1

I have this definition of an abstract class:

public abstract class Game<T> : IGame<T> where T : IGameItem
{
    public Guid Id { get; set; } = Guid.NewGuid();

    protected List<T> GameItems { get; set; } = new();

    public IReadOnlyList<T> Items => GameItems.AsReadOnly();

    public void AddGameItem(T gameItem)
    {
        if(gameItem is null)
        {
            throw new ArgumentNullException(nameof(gameItem));
        }
        this.GameItems.Add(gameItem);
    }

    public void RemoveGameItem(T gameItem)
    {
        if (gameItem is null)
        {
            throw new ArgumentNullException(nameof(gameItem));
        }

        this.GameItems.Remove(gameItem);
    }
}

where IGame is (IItem only contains a property Id of type Guid):

public interface IGame<T> : IItem where T : IGameItem
{
    IReadOnlyList<T> Items { get; }

    void AddGameItem(T gameItem);

    void RemoveGameItem(T gameItem);
}

where IGameItem is inherits from IItem but it´s empty for the moment.

Then I have the definition of IWordGame which is:

public interface IWordGame : IGame<IWordGameItem>
{
   *** Some properties here ***
}

And IWordGameItem inherits from IGameItem and adds some other properties.

The problem comes when I create a class that derives from Game

public class WordGame : Game<WordGameItem>, IWordGame
{
}

The class keeps complaining that I haven´t implemented the properties and methods of the IGame interface... But those methods are implemented in the Game base class... What am I missing? Do I need to restructure the arquitecture? What´s the best way to do it? If I implement the methods this is how the code goes:

IReadOnlyList<IWordGameItem> IGame<IWordGameItem>.Items => throw new NotImplementedException();

public void AddGameItem(IWordGameItem gameItem)
{
    throw new NotImplementedException();
}

public void RemoveGameItem(IWordGameItem gameItem)
{
    throw new NotImplementedException();
}

However I don´t want to implement the methods there but in the base class. How can I achieve that?

Jaime
  • 131
  • 9

1 Answers1

2

You need to change WordGame to:

public class WordGame : Game<IWordGameItem>, IWordGame
{
}

The thing is that Game<WordGameItem> is not IGame<IWordGameItem> (imagine that it would me true, then with class WordGameItem1 : IWordGameItem you would be able to call Game<WordGameItem>.AddGameItem(new WordGameItem1()) which obviously is not type safe, this is explained in more detail for example in this answer).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    Wow... it works but I don´t fully understand why. I though that I understood Covariance and Contravariance in generics but seems like I don´t. Thanks for sharing a link to another answer. – Jaime May 09 '23 at 21:45
  • 1
    @Jaime Was glad to help! There is no variance involved here. There is no variance for classes (hence `Game`) in C# and the `IGame` interface is invariant. So this works because `IWordGame` requires `IGame` to be implemented and `Game` as per definition implements it (i.e. simple generic type construction with `T` -> `IWordGameItem`). – Guru Stron May 09 '23 at 21:52
  • 1
    Ohhh now I get it... thank you very much. Very good explanation! – Jaime May 09 '23 at 21:56
  • In other words, the definition in your question was trying to extend a class that implements `IGame` and implement `IWordGame : IGame`, which you didn't do. – Jeremy Lakeman May 10 '23 at 01:29