Search code examples
c#constructortypesconstraintssubclassing

Constructor with an array of subclasses of an abstract class as a parameter


I'm working on a player inventory system for a game.

I have a struct Slot which has a List<Loot> collection that represents which kinds of items are allowed in it. The abstract class Loot is subclassed by all items which are lootable - i.e.: would be valid Content values for the Slot struct.

I want to express that a Slot can have restrictions on what subclasses of Loot it can contain. For instance, if the Slot represents an ammo container, I want it to only hold Loot subclasses which are ammo containers, like "Quivers" and "Shot Pouches" (which would subclass Container somewhere along the line).

Loot class

public abstract class Loot : GameEntity, ILootable
{
    public int MaxUnitsPerStack { get; set; }
    public int MaxUnitsCarriable { get; set; }
    public int MaxUnitsOwnable { get; set; }

    public void Stack() { }
    public void Split() { }
    public void Scrap() { }
}

Container class

public abstract class Container : Loot
{
    public List<Slot> Slots { get; set; }

    public Container(int slots)
    {
        this.Slots = new List<Slot>(slots);
    }
}

Slot struct

public struct Slot
{
    public Loot Content;
    public int Count;
    public List<Loot> ExclusiveLootTypes;

    public Slot(Loot[] exclusiveLootTypes)
    {
        this.Content = null;
        this.Count = 0;

        List<Loot> newList;
        if (exclusiveLootTypes.Count() > 0)
        {
            newList = new List<Loot>(exclusiveLootTypes.Count());
            foreach (Loot l in exclusiveLootTypes)
            {
                newList.Add(l);
            }
        }
        else { newList = new List<Loot>(); }
        this.ExclusiveLootTypes = newList;
    }
}

PlayerInventory

public struct PlayerInventory
{
    static Dictionary<Slot, string> Slots;

    static void Main()
    {
        Slots = new Dictionary<Slot, string>();

        /* Add a bunch of slots */
        Slots.Add(new Slot(/* I don't know the
                              syntax for this:
                              Quiver, Backpack */), "Backpack"); // Container
    }

}

I don't know how to provide the arguments for the Loot subclasses in the Slot constructor call in the Main method of the PlayerInventory class.

I hope this is clear. Thanks in advance.

EDIT

I was able to solve this (and by that, I mean get it to compile) using David Sieler's approach along with some Reflection.

Slot struct


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

    public struct Slot
    {
        private Loot _content;
        private int _count;
        public List ExclusiveLootTypes;

        public Loot Content
        {
            get { return _content; }
            private set
            {
                if ((ExclusiveLootTypes.Contains(value.GetType())) && (value.GetType().IsSubclassOf(Type.GetType("Loot"))))
                {
                    _content = value;
                }
            }
        }

        public int Count
        {
            get { return _count; }
            set { _count = value; }
        }

        public Slot(params Type[] exclusiveLootTypes)
        {
            this._content = null;
            this._count = 0;

            List newList;
            if (exclusiveLootTypes.Count() > 0)
            {
                newList = new List(exclusiveLootTypes.Count());
                foreach (Type l in exclusiveLootTypes)
                {
                    newList.Add(l);
                }
            }
            else { newList = new List(); }
            this.ExclusiveLootTypes = newList;
        }
    }

PlayerInventory call to Slot constructor


Slots.Add(new Slot(typeof(Backpack)));

Thanks again everyone for the discussion.


Solution

  • One approach would be to pass an array of Type objects to the Slot constructor:

    public Slot(Type[] exclusiveLootTypes) {
        // be sure and check that each Type is a subtype of Loot!
    }
    
    // Construction looks like:
    new Slot(new[] {GetType(AmmoContainer), GetType(GemContainer) /* or whatever */});
    

    Then write a property setter for Content that checks the type of the assigned object, and signals some sort of error if that type isn't contained in ExclusiveLootTypes.