Модел на наблюдател с делегат и събития, използващи масив от събития

Имаме клас, който управлява много опашки, които съхраняват данни. Искам потребителят да получава известия, когато към всяка от тези опашки се добавят нови данни. Бих искал да използвам модела на наблюдател, използвайки делегат и събития. Обикновено за едно събитие и източник бихме направили:

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, ...);
    

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

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 и се опитайте да разберете какво е наблюдателят и кои са субектите. Във вашия случай звучи така, сякаш опашките са субектите, не сте сигурни какви биха били наблюдателите във вашия модел на домейн.

След като направите това, нещата ще бъдат по-лесни за разбиране и е просто въпрос на внедряване на методите, декларирани от вашите интерфейси, например вашите теми (опашки) просто ще извикат notify (и ще предизвикат събитие, ако искате да използвате делегати), когато нещо се случва (елементът е добавен към опашката).

Надявам се това да помогне.

person JohnIdol    schedule 29.04.2011

Ето работния код на C#.

QueueManger излага събитие NewDataAddedEvent, което може да бъде абонирано от един или повече наблюдатели. Опашката извиква метод 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