2013-02-08 3 views
4

Я разрабатываю очень простую систему инвентаря для игры. Я столкнулся с препятствием, когда у меня есть инвентарь (массив определенного типа), который должен был бы принимать несколько типов объектов. Мой код:Как добавить несколько объектов в один массив?

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

Iweapon и IArmor нужно будет извлечь из IItem для того, чтобы ваш второй пример, чтобы работать , –

+0

Вы можете использовать «Generic List». Это позволит вам хранить всю информацию, связанную с объектом. – Greg

+0

[[Этот вопрос] (http: // stackoverflow.com/questions/14562047/generic-type-parameter-covariance-and-multiple-interface-implementation)] может дать вам некоторое вдохновение для определения ваших интерфейсов. –

ответ

11

Поскольку первый элемент будет всегда быть оружие, а второй всегда быть броня, вы не должны использовать массив (или любой другой структуры данных) на всех. У вас есть только два отдельных поля: оружие, оружие и другое.

private IWeapon weapon; 
private IArmour armor; 

public void initialiseInventory(IWeapon weapon, IArmour armour) 
{ 
    this.weapon = weapon; 
    this.armor = armor; 
} 
+4

Я не согласен с тем, что вы здесь поставили, если у него действительно есть только одно оружие и один бит доспехов, но вопросы, похоже, касаются вопроса о полиморфизме предметов ... –

+1

@ImmortalBlue Это, кажется, проблема в какой полиморфизм используется для решения проблемы, не подходящей для него. Если вы хотите продемонстрировать использование полиморфизма, найдите проблему, чтобы решить, к чему она эффективно относится. – Servy

0

Хотя я полностью согласен с ответом Servy дал:

Если вы действительно хотите использовать массив, нужно только добавить свойство Имя к интерфейсу IItem (или List<IITem>?).

interface IItem 
{ 
    string Name {get;set;} 
} 

Сомневаюсь, что это поможет вам в долгосрочной перспективе, поэтому я бы пошел на ответ Серви.

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]; 
    } 
    } 

} 
1

В родительский класс/интерфейс , вам нужно решить, какие общие операции/атрибуты действительно предназначены для этого.

Это может быть стоит иметь интерфейс что-то вроде этого:

Interface IItem 
{ 
    string name {get}; 
    string itemType {get}; 

} 

, то вы можете просто пойти

foreach(Iitem anItem in itemArray) 
{ 
    Console.WriteLine("The " + anItem.itemType + " is: " + anItem.Name); 
} 

Это не идеально, и поднимает вопросы о вашей модели, но это просто что-то думать о.

+0

Проблема в том, что предметы и оружие не будут по-настоящему взаимозаменяемыми, скорее всего. Они должны быть (в этом контексте), чтобы они работали. – Servy

+2

Это ответ на вопрос OPs – bas

+1

Ну, это основано на предположении, вопрос, похоже, о смешанных списках. Я решил, что комментарии являются временными, пока он пытается понять, как использовать смешанный список ... (Я чувствую запах инвентаря для приключенческой игры;)) –

0

Я обнаружил небольшую путаницу между интерфейсами и классами, но, тем не менее, вы должны просто убедиться, что у IItem есть свойство Name на нем (которое, если его интерфейс, IWeapon и IArmour потребуется реализовать), а не поместив свойство Name в каждый подкласс (нет такой вещи, как «subinterface» :)) Возможно, вам следует опубликовать код ваших интерфейсов/классов, хотя ....

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

Вы можете использовать оператор is, чтобы определить, реализует ли переменная конкретный интерфейс и затем переводит эту переменную в экземпляр этого интерфейса.

 if (inventory[0] is IWeapon) 
     { 
      IWeapon myWeapon = (IWeapon)inventory[0]; 
      Console.WriteLine("The weapon name is: " + myWeapon.Name); 
     } 
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; 
    } 
} 

Почему спроектировать класс для инвентаризации? Поскольку вы можете добавить такие вещи, как свойство TotalWeight или ItemCount, и т. Д., Проще, чем если бы у вас был только массив из IItem.

0

Предполагая, что вам действительно нужен массив для хранения различных предметов инвентаря (а не только двух отдельных полей), то, похоже, вы можете просто использовать наследование. Я разрешил добавлять различные свойства к оружию и доспеху, чтобы разъяснить варианты использования.

Базовый класс:

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); 
} 
+0

Вы можете использовать 'OfType' для своих запросов, а не' Where '' is'. – Servy

+0

Отредактировано ... Спасибо, что указали это. – blins

Смежные вопросы