Шаблон наблюдателя с делегатом и событиями с использованием массива событий

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

public delegate void NewDataAddedDelegate();
public event NewDataAddedDelegate NewDataAdded;

и для наблюдателя:

qManager.NewDataAdded += new qManager.NewDataAddedDelegate(getNewDataFunc);

Но в данном случае у нас есть, скажем, 10 очередей, в каждую из которых можно принимать данные произвольно. Поэтому мы хотели бы, чтобы функции наблюдателя подписывались на отдельную очередь. Мы думали, что можем сделать:

public delegate void NewDataAddedDelegate();
public event NewDataAddedDelegate [] NewDataAdded;  // can't do this

и в конструкторе qManager:

NewDataAdded = new NewDataAddedDelegate[numberOfQueues];

и в наблюдателе:

qManager.NewDataAdded[0] += new qManager.NewDataAddedDelegate(getNewDataFunc0);
qManager.NewDataAdded[1] += new qManager.NewDataAddedDelegate(getNewDataFunc1);

но нет, так как ожидается, что событие будет типом делегата, а не массивом типа делегатов.

Любые идеи о том, как подойти к этой проблеме?


person SomethingBetter    schedule 29.04.2011    source источник


Ответы (5)


Нет, события так не работают. Опции:

  • Создайте другой тип, который предоставляет событие, и создайте массив или коллекцию этого типа:

    // Preferably *don't* just expose an array...
    public TypeWithEvent[] Queues { get { ... } }
    
    // Subscription:
    qManager.Queues[i].NewDataAdded += ...
    
  • В качестве альтернативы не используйте события, а просто используйте метод:

    private NewDataAddededDelegate[] newDataAdded;
    
    public void SubscribeNewDataAddedHandler(int queue, 
                                             NewDataAddedDelegate handler)
    {
        newDataAdded[queue] += handler;
    }
    
    // Subscription
    qManager.SubscribeNewDataAddedHandler(0, ...);
    

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

person Jon Skeet    schedule 29.04.2011

Есть два подхода, которые вы можете использовать; первый должен иметь:

private NewDataAddedDelegate[] queues; // init not shown
public void Subscribe(int index, NewDataAddedDelegate handler) {
    queues[index] += handler;
}

и использовать

obj.Subscribe(index, ...);

но вы можете подумать о синхронизации и т. д. вокруг подписки. Второй подход заключается в создании класса-оболочки с событием, после чего вы можете использовать синхронизацию компилятора, которая хороша в C# 4.0:

public class SomeQueue {
    public event NewDataAddedDelegate NewDataAdded;
}

а затем выставить их, возможно, через индексатор, так что у вас есть

obj.Queues[index].NewDataAdded += ...

Лично я ожидаю, что первое будет проще. Мешать может только синхронизация. Я делаю это в каком-то коде pub-sub, и IIRC я ​​просто lock во время подписки.

person Marc Gravell    schedule 29.04.2011

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

Определите свои интерфейсы IObserver и ISubject и попытайтесь понять, что такое наблюдатель и что такое субъекты. В вашем случае звучит так, будто очереди являются субъектами, но не уверен, что наблюдатели будут в вашей модели предметной области.

Как только вы это сделаете, все станет проще понять, и это просто вопрос реализации методов, объявленных вашими интерфейсами, например, ваши субъекты (очереди) будут просто вызывать уведомление (и вызывать событие, если вы хотите использовать делегаты), когда что-то происходит (элемент добавлен в очередь).

Надеюсь это поможет.

person JohnIdol    schedule 29.04.2011

Вот рабочий код на C#.

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

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            QueueManager queueManager = new QueueManager();
            Observer observer = new Observer(queueManager);
            Queue queue1 = queueManager.AddQueue();
            Queue queue2 = queueManager.AddQueue();

            queue1.OnNewDataAdd();
            queue2.OnNewDataAdd();

            Console.ReadLine();
        }


        delegate void NewDataAddedDelegate(Queue queue);


        class Queue
        {
            QueueManager queueManager;
            public string id;
            public Queue(string id, QueueManager queueManager)
            {
                this.id = id;
                this.queueManager = queueManager;
            }

            public void OnNewDataAdd()
            {
                this.queueManager.NewDataAdded(this);
            }
        }

        class QueueManager
        {
            List<Queue> queues = new List<Queue>();

            public Queue AddQueue()
            {
                Queue queue = new Queue((queues.Count + 1).ToString(), this);
                this.queues.Add(queue);
                return queue;
            }

            public event NewDataAddedDelegate NewDataAddedEvent;
            public void NewDataAdded(Queue queue)
            {
                if (NewDataAddedEvent != null)
                    NewDataAddedEvent(queue);
            }
        }

        class Observer
        {
            public Observer(QueueManager queueManager)
            {
                queueManager.NewDataAddedEvent += new NewDataAddedDelegate(queue_NewDataAdded);
            }

            void queue_NewDataAdded(Queue queue)
            {
                Console.WriteLine("Notification to the observer from queue {0}", queue.id);
            }
        }

    }

}
person Sandeep G B    schedule 29.04.2011

Возможно, вы могли бы использовать шаблон агрегатора событий.

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

person Enyra    schedule 29.04.2011