我正在为游戏设计一个非常简单的库存系统。我遇到了一个障碍,我有一个需要接受多种类型对象的库存(一个特定类型的数组)。我的代码:
IWeapon[] inventory = new IWeapon[5];
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
inventory[0] = weapon; // Index 0 always contains the equipped weapon
inventory[1] = armour; // Index 1 always contains the equipped armour
}
我会得到一个错误,指出数组无法将装甲对象转换为武器对象(这是数组类型)。然后我想我可以创建IWeapon和IArmour将继承的超类(嗯,界面准确)。但后来又遇到了另一个错误...
IItem[] inventory = new IItem[5];
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
inventory[0] = weapon; // Index 0 always contains the equipped weapon
inventory[1] = armour; // Index 1 always contains the equipped armour
Console.WriteLine("The weapon name is: " + inventory[0].Name) // Problem!
}
由于数组类型是IItem,它只包含来自IItem的属性和方法,而不包含来自IWeapon或IArmour的属性和方法。因此问题在于我无法访问位于子类(子接口)IWeapon中的武器的名称。有没有办法我可以以某种方式重定向它以寻找子接口(IWeapon或IArmour)中的属性而不是超级接口(IItem)?我甚至走在正确的道路上吗?
答案 0 :(得分:11)
由于第一项总是是武器,第二项总是是盔甲,所以你根本不应该使用数组(或任何数据结构) 。只有两个单独的字段,一个持有武器,另一个持有装甲实例。
private IWeapon weapon;
private IArmour armor;
public void initialiseInventory(IWeapon weapon, IArmour armour)
{
this.weapon = weapon;
this.armor = armor;
}
答案 1 :(得分:2)
这是一个有趣(和常见)的谜题。你已经正确地弄清楚了它的第一部分:为了将元素存储在一个数组中,数组类型必须与进入数组的所有元素的共同祖先相匹配。当然,这将功能仅限于该共同祖先所提供的功能,这在您的情况下显然是不够的。
第二部分(即,一旦将所有元素都放在数组中,如何处理元素)会有点困难。您需要一个类型转换或多个分派。类型转换很简单:只需在元素前添加(IWeapon)
:
((IWeapon)inventory[0]).Name
对于多个项目,您可以使用LINQ:
foreach (IWeapon w in inventory.OfType<IWeapon>()) {
Console.WriteLine("The weapon name is: " + w.Name);
}
多次发送要复杂得多。它允许您根据多个对象创建虚拟方法。作为回报,你必须牺牲语言提供的简单性:调用方法需要制作特殊对象,而不是直接调用方法。请查看Visitor Pattern以了解如何处理多个调度的一些想法。
答案 2 :(得分:2)
您可以使用is
运算符来确定变量是否实现特定接口,然后将该变量强制转换为该接口的实例。
if (inventory[0] is IWeapon)
{
IWeapon myWeapon = (IWeapon)inventory[0];
Console.WriteLine("The weapon name is: " + myWeapon.Name);
}
答案 3 :(得分:1)
在父类/接口中,您需要确定真正意义上的常用操作/属性。
可能值得拥有这样的界面:
Interface IItem
{
string name {get};
string itemType {get};
}
然后你可以去
foreach(Iitem anItem in itemArray)
{
Console.WriteLine("The " + anItem.itemType + " is: " + anItem.Name);
}
它并不完美,并且对您的模型提出了疑问,但它只是需要考虑的事情。
答案 4 :(得分:0)
虽然我完全赞同Servy给出的答案:
如果你真的想要使用数组(或List<IITem>
?),你只需要将Name属性添加到IItem
接口。
interface IItem
{
string Name {get;set;}
}
我怀疑从长远来看它是否会对你有所帮助,所以我会选择Servy的答案。
答案 5 :(得分:0)
武器和装甲都有名字,所以这个属性应该放在IItem
界面。
此外,Servy所说的是有道理的,因为特定的位置总是具有相同类型的项目,因此为不同类型的项目设置数组没有多大意义。
如果要将它们作为数组访问,可以创建一个两个都有数组的类,并允许您访问具有特定类型的配备项:
public class Inventory {
public IWeapon CurrentWeapon { get; }
public IArmour CurrentArmour { get; }
private IItem[] _items = new IItem[8];
public IItem[int idx] {
get {
return
idx == 0 ? CurrentWeapon :
idx == 1 ? CurrentArmour :
idx_items[idx - 2];
}
}
}
答案 6 :(得分:0)
我发现接口和类之间有点混淆,但是,你应该非常简单地确保IItem上有Name属性(如果它是一个接口,IWeapon和IArmour需要实现),而不是放每个子类的Name属性(没有“子接口”:)) 也许你应该发布你的接口/类的代码....
答案 7 :(得分:0)
我必须加2美分,因为我发现这个问题很好,这可能是一个常见的问题。
我会选择这样的东西:
interface IItem
{
//some common properties / methods
}
interface IWEapon : IItem
{
string Name { get; set; } //maybe this should go to IItem? depends on your actual objects, of course
//some other wepaon specific properties / methods
}
interface IArmor : IItem
{
//some properties / methods
}
class Inventory
{
public Inventory(IWEapon startWeapon, IArmor startArmor)
{
CurrentWeapon = startWeapon;
CurrentArmor = startArmor;
//optional:
m_items.Add(startWeapon);
m_items.Add(startArmor);
}
private List<IItem> m_items = new List<IItem>();
IEnumerable<IItem> InventoryItems
{
get { return m_items; }
}
void AddItem(IItem item)
{
m_items.Add(item);
}
IWEapon CurrentWeapon
{
get;
set;
}
IArmor CurrentArmor
{
get;
set;
}
}
为什么要为库存设计课程?因为你可以比只有IItem
的数组更容易地添加像TotalWeight或ItemCount属性等的东西。
答案 8 :(得分:0)
假设您确实需要一个数组来存储不同的库存项目(而不仅仅是两个单独的字段),那么您似乎可以使用继承。我冒昧地为Weapon和Armor添加了不同的属性以澄清用例。
基类:
abstract class Item
{
public string Name { get; set; }
}
派生类型:
class Weapon : Item
{
public int Power { get; set; }
}
class Armor : Item
{
public int Resistance { get; set; }
}
使用示例:
Item[] inventory = new Item[2];
inventory[0] = new Weapon { Name = "Singing Sword", Power = 10 };
inventory[1] = new Armor { Name = "Chainmail Shirt", Resistance = 5 };
// Note below that the Name property is always accessible because it is defined in the base class.
// Traverse only Weapon items
foreach (Weapon weapon in inventory.OfType<Weapon>())
{
Console.WriteLine(weapon.Power);
}
// Traverse only Armor items
foreach (Armor armor in inventory.OfType<Armor>())
{
Console.WriteLine(armor.Resistance);
}
// Travers all items
foreach (Item item in inventory)
{
Console.WriteLine(item.Name);
}