I have two parent classes: Item
and ItemData
. From the first, I make items such as WeaponItem
and from the second I make corresponding data like WeaponData
.
Each item type takes the same type of data in its constructor (example: when creating WeaponItem
it needs WeaponData
).
I have the following method:
public static T CreateNewItem<T, DT>(ItemData data) where T : Item where DT : ItemData
{
if (data == null) return null;
if (data is DT dt)
{
//determine type of T
Type itemType = typeof(T);
if (itemType = typeof(WeaponItem))
{
//make a new weapon item...
WeaponItem new_weapon = new WeaponItem(dt); //error!
return new_weapon;
}
if (itemType = typeof(ArmorItem))
{
//make a new armor item...
}
//etc...
}
return null;
}
And I call it like so:
CreateNewItem<WeaponItem, WeaponData>(data);
Note: data
is of type ItemData
or some inherited type (eg. WeaponData
).
This method needs two types: one for the Item
and one for the ItemData
. I have several items (weapons, armors etc...) and I create the item based on the type I give. I want to also check for the data which I do with: data is DT dt
to get the WeaponData
from the data I pass, or null
if the cast fails.
When I try doing WeaponItem new_weapon = new WeaponItem(dt);
I get the following error:
Argument 1: cannot convert from 'DT' to 'WeaponData' [Assembly-CSharp]csharp(CS1503)
I tried casting dt
to WeaponData
like so: WeaponItem new_weapon = new WeaponItem((WeaponData)dt);
, but I get a similar error
Cannot convert type 'DT' to 'WeaponData' [Assembly-CSharp]csharp(CS0030)
Similarly, using data
in place of dt
produces errors.
Is it possible to solve this problem without checking the type every time?
CodePudding user response:
Your design is a bit counter intuitive to me, so let's start with an extremely simple version of the generic method and go from there. This code you can copy and compile.
You can also click this link and just hit the Run button.
I'd say the biggest advantage here is the generic method became one line of code. You can add as many items as you want and never change that method. Whereas your implementation gets more and more complex.
Code:
public class ItemData
{
public string ItemName { get; init; }
}
public class WeaponData : ItemData { }
public class Item
{
public ItemData Data { get; init; }
public Item(ItemData data)
{
Data = data;
}
}
public class WeaponItem : Item
{
public WeaponItem(WeaponData data) : base(data) { }
public override string ToString() => Data.ItemName;
}
public static T CreateNewItem<T, TD>(TD data)
where T : Item
where TD : ItemData
{
return (T)Activator.CreateInstance(typeof(T), data);
}
static void Main(string[] args)
{
var bestWeaponDataEver = new WeaponData() { ItemName = "Dragon Slayer" };
var amazingWeapon = CreateNewItem<WeaponItem, WeaponData>(bestWeaponDataEver);
Console.WriteLine(amazingWeapon);
}
Output:
Dragon Slayer
Another big difference is you're accepting the specific type ItemData
which should be the generic type DT
. Otherwise why does DT
even exist? This might have tripped you up.
public static T CreateNewItem<T, DT>(ItemData data) where T : Item where DT : ItemData
Should be this instead
public static T CreateNewItem<T, DT>(DT data) where T : Item where DT : ItemData
Note the constructor for WeaponItem
only accepts WeaponData
. So despite the simple generic method, you can't create a weapon with the base ItemData
or CakeData
or anything else.
If you're not sure how to expand on this comment and I'll try to help further.