C# List<baseClass> - any way to store inherited classes within, too?
Solution 1
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();
Solution 2
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
}
Solution 3
This is where Linq can be your friend when dealing with polymorphism. The problem with casting items is when the given index isn't the correct type. (I.e. inventory[0]
isn't a weapon
, (weapon)inventory[0]
will throw.)
You can avoid the exception by using:
var myWeapon = inventory[0] as weapon;
However then you will be doing #null checks everywhere. Using Linq you get a lot of flexibility:
var weapons = inventory.OfType<weapon>();
From here you can use Where
, FirstOrDefault
, etc. to find data you're interested or iterate over the weapons.
Deadrust
Updated on June 23, 2022Comments
-
Deadrust about 2 years
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 isweapon
.item
has properties (name, value, weight, "important item") that are also used withinweapon
, butweapon
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 aList<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 toweapon
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 asweapon
,armour
,accessory
etc. objects, which inherit fromitem
? 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
andweapon
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;} } }