Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# List<baseClass> - any way to store inherited classes within, too?

I am trying to create a simple "inventory" of items, much like in any RPG. I have very basic classes made, which have properties.

Anyway, I have a base class of item and inheriting from that is weapon. item has properties (name, value, weight, "important item") that are also used within weapon, but weapon has extra properties (attack, defense, speed, handedness).

I've got the following code (sorry if the readability is horrible):

static void Main(string[] args)
{   
     List<item> inventory = new List<item>();
     inventory.Add(new weapon("Souleater", 4000, 25.50f, false, 75, 30, 1.25f, 2));
                           //item--------------------------->  weapon--------->

     Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}",
            inventory[0].Name, inventory[0].BValue, inventory[0].Weight, inventory[0].Discard, 
            inventory[0].Atk, inventory[0].Def, inventory[0].Speed, inventory[0].Hands);

     Console.ReadLine();
}

Basically, what I'm trying to do is add a new weapon to the inventory, but the inventory is a List<item> type. I went out on a whim hoping that, due to it's inheritance, it'd be accepted. It was, but the properties specific to weapon aren't able to be accessed:

('shopSystem.item' does not contain a definition for 'Atk' and no extension method 'Atk' accepting a first argument of type 'shopSystem.item' could be found)

So, is there any way to achieve what I was intending here? Have an "inventory" which can store item objects, as well as weapon, armour, accessory etc. objects, which inherit from item? It is also worth mentioning that I can access all of the desired properties if I declare the following:

weapon Foo = new weapon("Sword", 200, 20.00f, false, 30, 20, 1.10f, 1);

Many thanks for reading.

Here's the item and weapon classes, if anybody is interested:

class item
{
    #region Region: Item Attributes
    protected string name = "";
    protected int baseValue = 0;
    protected float weight = 0.00f;
    protected bool noDiscard = false;
    #endregion

    public item(string n, int v, float w, bool nd){
        name = n; baseValue = v; weight = w; noDiscard = nd;}

    public string Name{
        get{return name;}
        set{if(value != ""){
            name = value;}
        }//end set
    }

    public int BValue{
        get{return baseValue;}
    }

    public float Weight{
        get{return weight;}
    }

    public bool Discard{
        get{return noDiscard;}
    }
}

class weapon : item
{
    #region Region: Weapon Attributes
    private int atk = 0;
    private int def = 0;
    private float speed = 0.00f;
    private byte hands = 0;
    #endregion

    public weapon(string n, int v, float w, bool nd, int a, int d, float s, byte h) : base(n, v, w, nd){
        atk = a; def =d; speed = s; hands = h;}

    public int Atk{
        get{return atk;}
    }

    public int Def{
        get{return def;}
    }

    public float Speed{
        get{return speed;}
    }

    public byte Hands{
        get{return hands;}
    }

}
like image 420
Deadrust Avatar asked Jul 15 '12 22:07

Deadrust


2 Answers

In order to access the properties specific to the inherited class, you need to cast to the appropriate concrete class type. So, rather than accessing inventory[0], you need to access (weapon)inventory[0].

Also, if you are performing common tasks that will have different implementations based on the underlying class, such as logging the values to the console (which I realize was done for testing, but is a good example anyway), you should implement an overridable method in the base class.

For example, in the base class:

public virtual void LogProperties()
{
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack",
            this.Name, this.BValue, this.Weight, this.Discard);
}

And in the weapon class:

public override void LogProperties()
{
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}",
            this.Name, this.BValue, this.Weight, this.Discard, 
            this.Atk, this.Def, this.Speed, this.Hands);
}

Then, to access this:

inventory[0].LogProperties();
like image 132
competent_tech Avatar answered Oct 24 '22 06:10

competent_tech


You are doing the right thing by using a List<item> to store items and subclasses of items!

When you extract the item from the list, you just have to do a bit of work to test if it's a weapon, or some other kind of item.

To see if the element at position i on the array is a weapon, use the as operator, which will return null if the item is not the specified type (perhaps it is armour or some other type of item). This is known as a dynamic cast. It works even if the item is null.

weapon w = inventory[i] as weapon;  // dynamic cast using 'as' operator
if (w != null)
{
     // work with the weapon
}

Another way to do this is to check the type using the is operator before doing a static cast.

if (inventory[i] is weapon)
{
     // static cast (won't fail 'cause we know type matches)
     weapon w = (weapon)inventory[i];           

     // work with the weapon
} 
like image 27
Monroe Thomas Avatar answered Oct 24 '22 07:10

Monroe Thomas