Accessing derived class variable within a list of parent class

I have a base class Item, from which I have a Weapon class derived.

The weapon class has a variable _isEquipped, however when I loop through my list I can’t access and query this. What is the correct way of doing this? What I currently do is simply:

for (int i = 0; i < itemList.Count; i++)
{
if (someVar == someOtherVar)
{
itemList[i]._isEquipped = true //Throws error CS1061: Type BaseItem does not contain a definition for '_isEquipped'...
}
}

Also, the variables in the derived classes are not exposed in the inspector. Is there also a way around this?

Unfortunately no. Unity’s serilisation system, which drives the inspector, does not support polymorphism.

You can always attempt to cast the base type to the derived type. But this is generally considered a bad idea. As a general rule a base class should have no knowledge of the classes that derive from it.

Not even with a CustomPropertyDrawer? (Not that i’m savvy with these, but something i’d be willing to learn if they can help)

I tried this, and it works as needed, but is this also considered a bad idea?

for (int i = 0; i < itemList.Count; i++)
{
if (someVar == someOtherVar)
{
                var eqpStatus = (itemList[i] as BaseWeapon).IsEquipped;
                eqpStatus = true;
                Debug.Log ("Equipped: " + eqpStatus.ToString ());
}
}

Thank you for your reply!

this:

var eqpStatus = (itemList[i] as BaseWeapon).IsEquipped;

Due to the fact you don’t check if the object IS a BaseWeapon, that implies you assume the list will always be full of objects typed BaseWeapon.

So why not make the list a list of BaseWeapons, rather than Item?

Otherwise… if itemList is NOT a BaseWeapon, what do you think will happen here?

1 Like

Consider what happens if itemList contains something that is not a BaseWeapon*.* In this case you will get an error. So you need to guard against this.

  1. Why put the if check inside of the loop? Do it before the loop as an early out before you waste any cycles going through a loop that does nothing.

  2. As lordofduct pointed out, if you expect the list to always contain BaseWeapons, make the list a list of BaseWeapons.

Otherwise, a safer way is to do this:

for (int i = 0; i < itemList.Count; ++i)
{
   var eqpStatus = false;
   var weapon = itemList[i] as BaseWeapon;
   if (weapon != null)
      eqpStatus = weapon.IsEquipped;

   Debug.Log("Equipped: " + eqpStatus);
}

i.e. Check that weapon is not null before using it.

Out of sheer curiosity, is this code objectively any better or worse, or just different?

for (int i = 0; i < itemList.Count; ++i)
{
   var eqpStatus = false;
   if (itemList[i] is BaseWeapon){
       eqpStatus = ((BaseWeapon)itemList[i]).IsEquipped;
   }
   Debug.Log("Equipped: " + eqpStatus);
}
1 Like

In my own code that’s exactly what I’ve done. Just typed an example up quickly on here.

Thanks for all of the advice. I don’t null check enough, something I need to concentrate on a lot more and get into the habit of doing. I like the last example you gave @BlackPete , and it will more than likely be what I use as my list will contain many more classes than just weapons.

It’s a habit of mine that I use “as” instead of “is” because I’m not too keen on having to cast it again after the if check.

If I don’t actually need to access anything in the object, then I’d use “is”, although that’s pretty rare.

That actually makes sense. I didn’t realise that the is method actually does an internal cast. I guess I assumed it was just magic.

In fact google seems to indicate that under the hood ‘is’ looks something like this:

operator is (var a, type b) {
    var c = a as b;
    return (c == null) ? false : true;
}

I’d always assumed it was the other way around.

1 Like