Конструктор с массивом подклассов абстрактного класса в качестве параметра

Я работаю над системой инвентаризации игроков для игры.

У меня есть структура Slot, в которой есть коллекция List‹Loot›, представляющая, какие виды предметов разрешены в ней. Абстрактный класс Loot является подклассом всех предметов, которые можно разграбить, т. е.: будут действительными значениями содержимого для структуры Slot.

Я хочу подчеркнуть, что Slot может иметь ограничения на то, какие подклассы Loot он может содержать. Например, если Slot представляет собой контейнер с боеприпасами, я хочу, чтобы он содержал только подклассы Loot, которые являются контейнерами с боеприпасами, такие как «Колчаны» и «Подсумки» (которые подкласс Container где-то в этой строке).

Класс добычи

public abstract class Loot : GameEntity, ILootable
{
    public int MaxUnitsPerStack { get; set; }
    public int MaxUnitsCarriable { get; set; }
    public int MaxUnitsOwnable { get; set; }

    public void Stack() { }
    public void Split() { }
    public void Scrap() { }
}

Класс-контейнер

public abstract class Container : Loot
{
    public List<Slot> Slots { get; set; }

    public Container(int slots)
    {
        this.Slots = new List<Slot>(slots);
    }
}

Структура слота

public struct Slot
{
    public Loot Content;
    public int Count;
    public List<Loot> ExclusiveLootTypes;

    public Slot(Loot[] exclusiveLootTypes)
    {
        this.Content = null;
        this.Count = 0;

        List<Loot> newList;
        if (exclusiveLootTypes.Count() > 0)
        {
            newList = new List<Loot>(exclusiveLootTypes.Count());
            foreach (Loot l in exclusiveLootTypes)
            {
                newList.Add(l);
            }
        }
        else { newList = new List<Loot>(); }
        this.ExclusiveLootTypes = newList;
    }
}

Инвентарь игрока

public struct PlayerInventory
{
    static Dictionary<Slot, string> Slots;

    static void Main()
    {
        Slots = new Dictionary<Slot, string>();

        /* Add a bunch of slots */
        Slots.Add(new Slot(/* I don't know the
                              syntax for this:
                              Quiver, Backpack */), "Backpack"); // Container
    }

}

Я не знаю, как предоставить аргументы для подклассов Loot в вызове конструктора Slot в методе Main класса PlayerInventory.

Я надеюсь, что это ясно. Заранее спасибо.

ИЗМЕНИТЬ

Я смог решить эту проблему (и под этим я подразумеваю компиляцию), используя подход Дэвида Зилера вместе с некоторым отражением.

Структура слота


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

    public struct Slot
    {
        private Loot _content;
        private int _count;
        public List ExclusiveLootTypes;

        public Loot Content
        {
            get { return _content; }
            private set
            {
                if ((ExclusiveLootTypes.Contains(value.GetType())) && (value.GetType().IsSubclassOf(Type.GetType("Loot"))))
                {
                    _content = value;
                }
            }
        }

        public int Count
        {
            get { return _count; }
            set { _count = value; }
        }

        public Slot(params Type[] exclusiveLootTypes)
        {
            this._content = null;
            this._count = 0;

            List newList;
            if (exclusiveLootTypes.Count() > 0)
            {
                newList = new List(exclusiveLootTypes.Count());
                foreach (Type l in exclusiveLootTypes)
                {
                    newList.Add(l);
                }
            }
            else { newList = new List(); }
            this.ExclusiveLootTypes = newList;
        }
    }

Вызов PlayerInventory для конструктора Slot


Slots.Add(new Slot(typeof(Backpack)));

Еще раз спасибо всем за обсуждение.


person Community    schedule 13.12.2009    source источник
comment
Подумав об этом всего около дня, экземпляры Loot представляют собой тип добычи. У самого слота есть свойство Count, которое показывает, сколько в нем лута. Я полностью ожидаю изменить это дюжину раз, но это то, как это работает в настоящее время. Хорошая отсылка к TES :)   -  person    schedule 13.12.2009
comment
Извините, я удалил комментарий с вопросом, представляют ли экземпляры Loot тип добычи или реальную добычу, потому что я забыл идею, которая у меня была при написании. Таким образом, кажется, вам просто нужно передать возможные типы добычи конструктору Slot, как показано в ответе @Anon, хотя, вероятно, существует много типов добычи, которые можно поместить в рюкзак. Вы думали об интерфейсе, который реализует типы добычи, которые можно поместить в рюкзак?   -  person dtb    schedule 13.12.2009
comment
Что ж, список ExclusiveLootTypes представляет собой набор ограничений на содержимое слота, а не диапазон возможного содержимого. Если в этом списке были ЛЮБЫЕ записи, то слот мог содержать ТОЛЬКО элементы того типа(ов) в списке. Однако, если список пуст, он может содержать что угодно. Я еще не написал эту процедуру, но это идея. Все подклассы лута можно поместить в рюкзак — это одна из вещей, которая делает их лутом.   -  person    schedule 13.12.2009
comment
Но вы не можете положить даэдрический короткий меч в колчан. ... List<Loot> очень похоже на список типов лута, который может быть возможным содержимым слота, не так ли?   -  person dtb    schedule 13.12.2009
comment
Это правда. Как вы думаете, это то, что я мог бы решить, правильно прокомментировав это свойство? Или это указывает на какой-то более фундаментальный недостаток?   -  person    schedule 13.12.2009
comment
Я нахожу ваше имя переменной ExclusiveLootTypes очень запутанным :-) Я думаю, вам следует взглянуть на ответ @Cameron MacFarland; это кажется лучшим решением. С другой стороны, я бы, наверное, отказался от концепции слотов. В любом случае, все слоты в колчане равны, верно?   -  person dtb    schedule 13.12.2009
comment
Да, но не все слоты в PlayerInventory есть. Слот для сумки с боеприпасами принимает только Quiver, ShotPouch и т. д. Сам Quiver имеет слоты, а объект Quiver также имеет список ограничений типа, так что любой контейнер (из слотов) может глобально ограничивать, какие предметы он может содержать или внутри этого контейнера. , каждый слот может ограничивать свой собственный контент. Таким образом, слот одинаково хорошо работает как для PlayerInventory, так и для любого типа сумки. Просто показался мне более гибким.   -  person    schedule 13.12.2009
comment
Я бы сказал, что правая рука — это контейнер для меча, спина — контейнер для колчана, а колчан — контейнер для стрел. Существует два разных типа контейнеров: те, которые ограничивают свое содержимое по количеству (правая рука может держать только 1 меч), ​​и те, которые ограничивают свое содержимое по весу (рюкзак может вместить любое количество предметов, если их размер не превышает предел веса рюкзака). Я лично ненавижу игры, в которых есть только ограниченное количество предметов в рюкзаке.   -  person dtb    schedule 13.12.2009
comment
Чего вы пытаетесь достичь с помощью слотов? Что, если вы просто использовали список вместо слотов? Если слот — это просто список с ограничениями, то слот может наследоваться от System.Collections.ObjectModel.Collection‹T›, переопределить InsertItem и добавить туда ограничение.   -  person Cameron MacFarland    schedule 13.12.2009
comment
Я бы сказал, что контейнер — это словарь, в котором хранятся типы добычи и количество фактических предметов каждого типа в контейнере, то есть Dictionary<Loot,int>.   -  person dtb    schedule 13.12.2009
comment
Я думаю, что словарь - неправильный подход. Вместо этого в инвентаре игроков должно быть свойство RightArmSlot типа Slot‹IRightArm›, LeftArmSlot типа Slot‹ILeftArm› и т. д. Таким образом, каждый слот ограничен по типу напрямую, а не все слоты выглядят так, как будто они могут содержать любой тип.   -  person Cameron MacFarland    schedule 13.12.2009
comment
@Cameron: Я думаю, что я пытаюсь достичь с помощью слотов, это объект, который может определять различные ограничения на типы элементов, которые он может содержать, и некоторые функции для работы с его содержимым (стек, разделение, лом и т. д.) Слот пришел на ум как простая метафора/объект-контейнер для всех этих вещей. Таким образом, я мог повторно использовать его для всего, что было контейнером подклассов Loot. Как и в случае с Point(x,y) - проще думать о ней как о структуре, чем о беспорядке операций со списками.   -  person    schedule 13.12.2009
comment
Хммм ладно. Могли бы вы добиться того же (операций со списками) с помощью метода расширения?   -  person Cameron MacFarland    schedule 13.12.2009
comment
Вы можете использовать их только в статических классах, верно? Не ограничит ли это мою гибкость? Я знаю, что вы, вероятно, только что заглянули, чтобы обсудить что-то очень конкретное, например, синтаксис, но не могли бы вы придумать лучший общий подход к проблеме Player-contains-loot (некоторые из которых сами являются контейнерами с предметами добычи)?   -  person    schedule 13.12.2009
comment
Методы расширения работают только с экземплярами, а не со статическими классами. Они определены в статическом классе, но для работы им нужен экземпляр. Я обновлю свой ответ, чтобы ответить на ваш вопрос о дизайне.   -  person Cameron MacFarland    schedule 13.12.2009


Ответы (4)


Одним из подходов может быть передача массива объектов Type в конструктор Slot:

public Slot(Type[] exclusiveLootTypes) {
    // be sure and check that each Type is a subtype of Loot!
}

// Construction looks like:
new Slot(new[] {GetType(AmmoContainer), GetType(GemContainer) /* or whatever */});

Затем напишите установщик свойств для Content, который проверяет тип назначенного объекта и сигнализирует о какой-либо ошибке, если этот тип не содержится в ExclusiveLootTypes.

person David Seiler    schedule 13.12.2009

Вероятно, вам будет проще использовать params в определении конструктора Slot:

Slot(params Loot[] exclusiveLootTypes)

Это позволит вам называть это так:

new Slot(lootItem1, lootItem2, lootItem2 /*...etc*/);

В противном случае вам нужно будет создать массив и передать его.

person Anon.    schedule 13.12.2009
comment
Я надеялся справиться с этим таким образом, но это позволяет мне использовать только экземпляры, а не типы. Вышеупомянутый новый Slot(lootItem1...) будет передавать существующие экземпляры. Я хочу передать типы, такие как: ‹pre›‹code› новый слот (колчан, ShotPouch); ‹/code›‹/pre›, где Quiver и ShotPouch будут подклассами Loot. - person ; 13.12.2009

Похоже, дизайн вашего объекта нуждается в небольшой корректировке.

Что, если бы типы предметов добычи также были интерфейсами, например, все предметы боеприпасов наследуются от IAmmoContainer.

Затем вы можете передать тип IAmmoContainer, чтобы ограничить слот.

public class Quiver : Container, IAmmoContainer
public class ShotPouch : Container, IAmmoContainer
// ...
new Slot(typeof(IAmmoContainer))

ИЗМЕНИТЬ

Основываясь на обсуждении в комментариях, вот как я буду разрабатывать эту систему.

Класс Loot в порядке. Он представляет собой основу иерархии добычи.

Затем вы определяете интерфейсы для «слотов», которые может занимать предмет. Например:

public class LongSword : Loot, IRightHandItem
public class ShortSword : Loot, IRightHandItem, ILeftHandItem

Затем класс PlayerInventory имеет «слоты» в качестве свойств, которые ограничены соответствующим типом.

public class PlayerInventory
{
    public List<IRightHandItem> RightHandSlot { get; private set; }
    public List<ILeftHandItem> LeftHandSlot { get; private set; }
    // etc...
}
person Cameron MacFarland    schedule 13.12.2009
comment
Slots.Add(новый слот(IAmmoBag), рюкзак); выдает ошибку: Аргумент '1': невозможно преобразовать из 'System.Type' в (пространство имен).Loot[]'. - person ; 13.12.2009
comment
Хорошо, теперь я действительно смущен. Итак, ваш слот уже содержит экземпляр типов, которые он может содержать? Вы пытаетесь ограничить типы, добавленные в слот, или предварительно инициализировать слот уже существующими типами? - person Cameron MacFarland; 13.12.2009
comment
Изначально — на данный момент — нет, слот ничего не содержит (его переменная Content, которая в настоящее время содержит тип лута == null.) Я пытаюсь сказать, что если в списке есть ЛЮБЫЕ предметы, затем слот будет запускать проверку (когда я его кодирую), чтобы гарантировать, что переменная Content ТОЛЬКО получает элемент, который является подклассом одного из типов в List‹Loot› ExclusiveLootTypes. Итак, я хочу создать слот и передать ему ряд подклассов лута, а затем он добавит эти типы в список «Лут» ExclusiveLootTypes. - person ; 13.12.2009
comment
Чтобы уточнить, я не хочу передавать экземпляры классов в слот, я хочу передавать подклассы абстрактного класса Loot. Затем слот гарантирует, что его переменная Content будет заполнена только экземплярами, которые относятся к подклассу (ам), перечисленным в List‹Loot› ExclusiveLootTypes. - person ; 13.12.2009
comment
@your Edit: я не думаю, что это представляет то, чего я пытаюсь достичь. При таком подходе PlayerInventory будет иметь контейнер (List‹IRightHandItem›RightHandSlot), который будет содержать любое количество, скажем, LongSwords одновременно. Ваша версия слота RightHand больше похожа на сумку. Я определил слот как контейнер, который может содержать ТОЛЬКО ОДИН предмет — будь то сумка, меч или что-то еще. Тот единственный предмет, который он может содержать, может быть ограничен его типом. Таким образом, слот RightHand будет содержать только один предмет; ТИП этого предмета - какой это подкласс лута, определяется в списке ExclusiveLootType слота. - person ; 13.12.2009

и чтобы объединить предыдущее предложение по использованию параметров, вы можете использовать расширение LINQ для преобразования параметров в список за один шаг:

public Slot(params Type[] exclusiveLootTypes)
{
    this.Content = null;
    this.Count = 0;

    this.ExclusiveLootTypes = exclusiveLootTypes.ToList();
}
person charoco    schedule 13.12.2009