I'm having a trouble understanding the following concept:
I'm trying to extend a base class with following properties:
public class MovingItem : Item
{
public Vector2 Acceleration { get; set; }
public Vector2 Velocity { get; set; }
public Vector2 Position { get; set; }
}
and i have several Item
classes describing different items e.g. Sphere
, Weapon
, Sprite
etc., etc., they inherently have different logic to them, but they are all Items
in this case, because MovingItem
and aforementioned Items
are on the 'same level' in inheritance tree, i cannot assign a variable like this:
MovingItem mi = new Weapon() { ... };
Because obviously, Weapon
is Item
, so is MovingItem
but the Weapon
is not MovingItem
So obvious solution would be to make those Item
s to inherit MovingItem
instead of Item
but i could think about 2 problems with this kind of solution:
Controls
in some UI library)Items
logic in a inherently different way than MovingItem
, like PickupableItem
I thought that something like that could work:
public class MovingItem<T> : T where T : Item
{
public Vector2 Acceleration { get; set; }
public Vector2 Velocity { get; set; }
public Vector2 Position { get; set; }
}
I thought this could help me extend not the Item
directly, but wrap those implementations, and not having to create possibly hundreds of classes by hand...
But unfortunately, it is not possible, compiler says that class can't inherit from type parameter:
CS0689: Cannot derive from 'T' because it is a type parameter
Ultimately i would like to achieve a solution where i could create a MovingItem
object from other Item
object, and then operate, like this MovingItem
is an Item
like so...
public class ItemCollection { ...
public void Add(Item item) { ... }
}
...
MovingItem<Weapon> movingWeapon = new Weapon(); { ... }
itemsCollection.Add(movingWeapon); // .Add(...) accepts argument of type Item
So... is there a solution to this kind of problem, extend Item
while still being an Item
for all other classes that inherit it?
How do i approach this?
In my experience, Composition is almost always preferable to Inheritance.
public class Weapon : Item { }
public class MovementState
{
public Vector2 Acceleration { get; set; }
public Vector2 Velocity { get; set; }
public Vector2 Position { get; set; }
}
public class MovingItem
{
public required Item Item {get;set;}
public required MovementState MovementState {get;set;}
}
var movingItems = new List<MovingItem>();
var movingWeapon = new MovingItem
{
Item = new Weapon
{
// ...
},
MovementState = new MovementState
{
// ...
}
};
movingItems.Add(movingWeapon);
The same pattern can be repeated for other purposes, like PickupableItem:
public class PickupableItem
{
public required Item Item {get;set;}
public Hero? HeldBy {get;set;}
}
This follows the Open/Closed Principle by allowing you to create new concepts like movement, holdability, etc., without having to change the original classes. This reduces coupling by making it so that code that just cares about a weapon's attributes doesn't also need to be exposed to details like whether it's being held or whether it's moving.