Помещение различных типов c в структуру данных, а затем обработка их при удалении - PullRequest
0 голосов
/ 20 января 2020

Я вчера отправил этот вопрос, но он был помечен как дубликат C# - Несколько обобщенных c типов в одном списке

Чтобы уточнить, ответ в этой проблеме показывает Как правильно разместить несколько обобщенных типов c в одном списке. Однако это не моя проблема. Моя проблема в том, что после удаления элементов из списка мне нужно знать, к какому типу они относятся, чтобы правильно их обрабатывать.

В качестве отправной точки я использую ответ на поставленный выше вопрос, поскольку у него есть хорошее решение для размещения элементов нескольких типов в структуре данных. Как вы можете видеть ниже, я создал абстрактный базовый класс ScheduleItem, а затем наследующий класс generi c ScheduleItem. Это позволяет мне переходить к различным типам c в одну и ту же очередь приоритетов.

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


public class ScheduleManager
{
    PriorityQueue<ScheduleItem> schedule = new PriorityQueue<ScheduleItem>();

    //case: adding an item with int data
    public void AddScheduleItem(int steps, string eventName, int param) {
        schedule.Enqueue(new ScheduleItem<int>(eventName, param, steps), steps);
    }

    //case: adding an item with bool data
    public void AddScheduleItem(int steps, string eventName, bool param)
        schedule.Enqueue(new ScheduleItem<bool>(eventName, param, steps), steps);
    }

    public ScheduleManager()
    {
        schedule.Enqueue(new ScheduleItem<bool>("test", true, 1), 1);
        ScheduleItem test = schedule.Dequeue();

        Debug.Log(test.ScheduledStep); //Coming up null
        Debug.Log(test.Data); //Coming up null
        test.Activate(); //Causing error
    }

    //The abstract base class
    abstract class ScheduleItem { }

    //The inheriting generic class. This is the one that actually 
    //holds data I need
    class ScheduleItem<T> : ScheduleItem where T : struct {
        public T Data { get; private set; }
        public string EventName { get; private set; }
        public int ScheduledStep { get; private set; }

        public ScheduleItem(string eventName, T data, int scheduledStep) {
            EventName = eventName;
            Data = data;
            ScheduledStep = scheduledStep;
        }

        public void Activate() {
            if(typeof(T) == typeof(int)) {
                int data = (int)Convert.ChangeType(Data, typeof(int));
                EventManager.TriggerIntEvent(EventName, data);
            }

            if (typeof(T) == typeof(bool))
            {
                bool data = (bool)Convert.ChangeType(Data, typeof(bool));
                EventManager.TriggerBoolEvent(EventName, data);
            }
        }
    }
}

Любая помощь будет принята с благодарностью!

1 Ответ

4 голосов
/ 20 января 2020

Вернуться к OOP Основы.

Вы хотите, чтобы EventName и ScheduledStep были доступны независимо от типа ScheduleItem. Поэтому они должны быть в самом классе ScheduleItem.

Вы также хотите, чтобы поведение каждого подкласса ScheduleItem было различным в зависимости от типа его данных. Самый простой способ сделать это - иметь другой подкласс для каждого типа данных, который имеет другой метод Activate.

public abstract class ScheduleItem
{
    public string EventName { get; }
    public int ScheduledStep { get; }

    protected ScheculeItem(string eventName, int scheduledStep)
    {
        EventName = eventName;
        ScheduledStep = scheduledStep;
    }
    public abstract void Activate();
}

public class IntScheduleItem
{
    private readonly int data;
    public IntScheduleItem(string eventName, int scheduledStep, int data)
        : base(eventName, int scheduledStep)
    {
        this.data = data;
    }

    public override void Activate()
    {
         EventManager.TriggerIntEvent(EventName, data);
    }
}

... and so on

Если единственное различие между различными типами ScheduleItem это метод EventManager, который вызывается, вы могли бы сделать что-то подобное, вместо того, чтобы иметь отдельные IntScheduleEvent, BoolScheduleEvent, et c, классы:

public class ScheduleItem<T> : ScheduleItem
{
    private readonly T data;
    private readonly Action<string, T> activator;

    public ScheduleItem(string eventName, int scheduledStep, int data, Action<string, T> activator)
        : base(eventName, int scheduledStep)
    {
        this.data = data;
        this.activator = activator;
    }

    public override void Activate()
    {
        activator(EventName, data);
    }
}

...

public void AddScheduleItem(int steps, string eventName, int param) {
    schedule.Enqueue(new ScheduleItem<int>(eventName, steps, param, EventManager.TriggerIntEvent), steps);
}

Вы могли бы даже сделайте еще один шаг и покончите с иерархией классов:

public class ScheduleItem
{
    public string EventName { get; }
    public int ScheduledStep { get; }
    private readonly Action activator;

    protected ScheculeItem(string eventName, int scheduledStep, Action activator)
    {
        EventName = eventName;
        ScheduledStep = scheduledStep;
        this.activator = activator;
    }
    public void Activate() => activator();
}

...

public void AddScheduleItem(int steps, string eventName, int param) {
    schedule.Enqueue(new ScheduleItem(eventName, steps, () => EventManager.TriggerIntEvent(eventName, param)), steps);
...