user164226
user164226

Reputation:

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.

Upvotes: 2

Views: 882

Answers (4)

Cameron MacFarland
Cameron MacFarland

Reputation: 71856

It sounds like your object design needs a slight adjustment.

What if the types of loot items were also interfaces, so for example, all ammo loot items inherit from IAmmoContainer.

Then you could pass in the IAmmoContainer type to restrict the slot.

public class Quiver : Container, IAmmoContainer
public class ShotPouch : Container, IAmmoContainer
// ...
new Slot(typeof(IAmmoContainer))

EDIT

Based on the discussion in the comments, here's how I'd go about designing this system.

The Loot class is fine. It represents the base of the loot hierarchy.

Then you define interfaces for the "slots" that an item can occupy. For example:

public class LongSword : Loot, IRightHandItem
public class ShortSword : Loot, IRightHandItem, ILeftHandItem

Then the PlayerInventory class has "slots" as properties, that are restricted to the appropriate type.

public class PlayerInventory
{
    public List<IRightHandItem> RightHandSlot { get; private set; }
    public List<ILeftHandItem> LeftHandSlot { get; private set; }
    // etc...
}

Upvotes: 2

charoco
charoco

Reputation: 133

and to combine the previous suggestion to use params, you can use a LINQ extension to convert the params into a List in one step:

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

    this.ExclusiveLootTypes = exclusiveLootTypes.ToList();
}

Upvotes: 1

David Seiler
David Seiler

Reputation: 9705

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.

Upvotes: 1

Anon.
Anon.

Reputation: 59973

You'd likely find it easier to use params in the definition for the Slot constructor:

Slot(params Loot[] exclusiveLootTypes)

This would allow you to call it like:

new Slot(lootItem1, lootItem2, lootItem2 /*...etc*/);

Otherwise, you'd need to create an array and pass it in.

Upvotes: 2

Related Questions