Распространение событий в коллекционном проекте - PullRequest
1 голос
/ 20 июля 2010

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

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

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

редактирует

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

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");
        }
    }

И когда я добавляю канал на карту в классе карт, я звоню:

channel.ChannelPropertyChanged += this.OnChannelPropertyChanged;

Ответы [ 2 ]

3 голосов
/ 20 июля 2010

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

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

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

system.FrameEvent += Handler;

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

2 голосов
/ 20 июля 2010

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

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

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

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

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

...