2

I need to keep several children of a base class in a list. Normally not an issue, but since Generics are involved this became a head-scratcher for me. My base class looks like this:

public abstract class Equipment<T> where T:EquipmentData { }

Now a child may look like:

public class Weapon : Equipment<WeaponData> { }

with

public class WeaponData : EquipmentData { }

Analogous there's also:

public class Shield : Equipment<ShieldData> { }
public class ShieldData : EquipmentData { }

Now I need a list that contains children of Equipment<T> but apparently List<T>requires me to be more specific, like List<Equipment<EquipmentData>>. However when I try and add a Weaponobject to that list, I get the error that it's not possible to convert Weapon into Equipment<EquipmentData>. Of course changing the list to List<Equipment<WeaponData>> will work, but then I won't be able to add Shieldobjects to that list for the same (obvious) reason.

In essence my question is if there's some way to make the List a

List<Equipment<child ofEquipmentData>> so that it would take Weapon objects as well as Shieldobjects?

Rand Random
  • 7,300
  • 10
  • 40
  • 88
Wolf
  • 29
  • 2
  • 5
    This is a bad use of generics. Don't do it. As you've discovered, it causes problems when you try to combine kinds of polymorphism. See my series https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/ for more thoughts on this pattern specifically, and similar patterns for representing weapons in FRP games. – Eric Lippert Jan 17 '18 at 09:27
  • Any time you have a pattern where there are two class hierarchies that have to be kept in sync and related to each other by constraints, you'll have problems like this. Parameterizing the base class buys you *nothing*. – Eric Lippert Jan 17 '18 at 09:31
  • If you insist, see first duplicate for an option. You would have to create a non-generic base type for `Equipment`, so all your classes shared a type they are all compatible with. But IMHO that's a terrible way to go (sorry Marc). See second marked duplicate for information about why you don't want to do this, and of course Eric's very good series that expounds on the topic. – Peter Duniho Jan 17 '18 at 09:32
  • @EricLippert I see what you mean. It's actually not a synced hierarchy, though. Usually all `Equipment` share the same `EquipmentData` it's just in a few cases when extra data is needed (like the visuals of a shot (shot, trail, impact) or the speed of the bullet from a weapon or the visuals of a shield around the ship (model, material)) that I need a child of `EquipmentData`. I try to keep `EquipmentData` and the special data from the Weapon in the same object for workflow reasons to make manipulation in balancing - through the Unity Inspector - easier. – Wolf Jan 17 '18 at 11:22

1 Answers1

1

I suspect the only way to do that would be to add a common non-generic base-class, perhaps:

public abstract class Equipment { }
public abstract class Equipment<T> : Equipment where T : EquipmentData { }

using List<Equipment>, where Equipment has whatever base functionality you need access to without knowing the types. If you add any non-specific members to the base-class, you can use member-hiding (new) to re-declare them with more specific types in the Equipment<T> type.

You could do the same thing with an IEquipment interface, if you prefer.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    Since the problem is that the types in question cannot vary in a covariant manner when used in a list, likely they'd have to be interfaces. But I think the better design is to not have this relationship between two parallel class hierarchies at all. – Eric Lippert Jan 17 '18 at 09:29