Распространение событий в проекте на основе коллекции

У меня есть проект на основе коллекции в .NET 4. Я имею в виду, что у меня есть основная коллекция, называемая "Система", которая состоит из фреймов, каждая из которых состоит из карточек, которые, в свою очередь, сделаны вверх каналов. Итак, это выглядит как Система-> Фрейм-> Карта-> Канал. Все они представлены в виде объектов, и между ними существует связь «родитель-ребенок». По сути, Channel доступен только Card, Card доступен только Frame, а Frame доступен только System.

В идеале я хотел бы предоставлять внешнему миру методы только из класса System. Однако в классах Channel, Card и Frame происходят важные события. В настоящее время я обращаюсь с ними через размножение. Предположим, в канале произошло событие. Это событие сначала возникает в Card, затем в Frame и, наконец, в System. Вы можете видеть, как это приводит к большому количеству кода. Но меня больше всего беспокоит не код, а производительность.

Как вы думаете, это распространение плохо влияет на мою производительность? Есть ли способ сделать его более эффективным? Какие еще у меня есть варианты? Мои коллекции относительно небольшие. Система - 1, Фреймы ‹16, Карты‹ 256, Каналы ‹8192. Большая часть данных хранится в классе Channel, в котором есть только примитивные объекты.

РЕДАКТИРОВАТЬ

Вот код, который у меня есть в Card для события, вызываемого каналом:

protected virtual void OnChannelPropertyChanged(Object sender, PFPropertyChangedEventArgs e)
    {
        try
        {
            EventHandler<PFPropertyChangedEventArgs> handler = ChannelPropertyChanged;
            TestEventArgs_ChannelPropertyChanged = e;
            if (handler != null)
            {
                handler(sender, e);
            }
        }
        catch (Exception ex)
        {
            Milltown.MTCore.mtException mtEx = new Milltown.MTCore.mtException((int)PFExceptions.Exception_Hidden_FuctionLevel, ex,
            PFCommonVariables.ApplicationPlatform, PFCommonVariables.ApplicationDataSource, "PFCard:OnChannelPropertyChanged");
        }
    }

И когда я добавляю канал к Card в классе Card, я вызываю:

channel.ChannelPropertyChanged += this.OnChannelPropertyChanged;

person sbenderli    schedule 20.07.2010    source источник


Ответы (2)


Единственный способ ответить, влияет ли это на вашу производительность, - это проверить ее: попробуйте один способ, когда вы распространяете события, а затем другой, где вы подключаетесь напрямую. Посмотрите, что быстрее.

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

Я бы порекомендовал ваш подход, если бы кто-нибудь спросил, как делать то, что вам нужно. Продолжайте, если это не проблема.

ИЗМЕНИТЬ

В ответ на ваш комментарий к другому ответу я хотел расширить. События C # имеют так называемый «синтаксис свойств». Если реализовано явно в классе, это выглядит примерно так:

private EventHandler eventDelegate;

public event EventHandler MyEvent
{
    add { eventDelegate += value; }
    remove { eventDelegate -= value; }
}

(На самом деле он использует Delegate.Combine, но здесь это несущественно)

Когда вы присоединяетесь к событию, он фактически вызывает код add, передавая делегат для присоединения как value; то же самое верно и для remove.

Когда вы используете такой сокращенный синтаксис событий:

public event EventHandler MyEvent;

На самом деле он генерирует скрытый код, очень похожий на то, что я опубликовал выше. Однако, если вы сделаете это явным образом, вы сможете делать все, что захотите, в обработчиках add и remove. Это означает, что вы можете перенаправить целевого делегата в другое место. В случае другого ответа он перенаправляет делегата на дочерний объект.

Это будет работать тогда и только тогда, когда выполняются следующие условия:

  1. Между родителем и потомком существует соотношение 1: 1, и связанный объект не изменится.
  2. Предоставление ссылки на дочерний объект (как object) допустимо

Если у вас несколько дочерних элементов, это технически возможно (вы можете просто выполнить цикл и присоединить ко всем из них в add и удалить их всех в remove), но с этим гораздо труднее справиться. Если связанный объект или набор объектов могут измениться после присоединения события, то координация становится практически невозможной.

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

person Adam Robinson    schedule 20.07.2010

Вы можете сделать это в классе System:

event EventHandler FrameEvent
{
    add { this.frame.FrameEvent += value; }
    remove { this.frame.FrameEvent -= value; }
}

Итак, если клиент делает это

system.FrameEvent += Handler;

Обработчик действительно привязан к событию в классе Frame.

person Henrik    schedule 20.07.2010
comment
что вы имеете в виду "действительно" привязанный? - person sbenderli; 20.07.2010
comment
Решение Хенрика остроумное. С одной стороны, событие отображается на системном уровне. С другой стороны, это реализовано на уровне кадра, без повторной сигнализации о событии. - person Steven Sudit; 20.07.2010
comment
Если между родителем и потомком есть соотношение 1: 1, то это сработает. Если, однако, вы хотите, чтобы одно событие запускалось, когда какой-либо дочерний объект запускает событие, вам придется использовать решение, которое у вас есть сейчас. Обратите внимание, что это также (если соблюдаются стандартные методы обработки событий) предоставит ссылку на дочерний класс через свойство sender аргументов события, поскольку дочерний класс будет напрямую сигнализировать потребителю. - person Adam Robinson; 20.07.2010
comment
Может ли кто-нибудь подробнее рассказать о различиях между решением Хенрика и тем, что у меня сейчас есть для распространения событий? Пожалуйста, посмотрите мои изменения для моего кода. - person sbenderli; 20.07.2010
comment
@sbenderli: Разработка слишком длинная для комментария; см. редактирование моего ответа для более подробного объяснения. - person Adam Robinson; 20.07.2010