Распространение событий в коллекционном проекте
У меня есть проект на основе коллекций в.NET 4. Я имею в виду, что у меня есть основная коллекция, называемая "Система", которая состоит из фреймов, каждый из которых состоит из карточек, которые, в свою очередь, сделаны вверх каналов. Итак, это выглядит как System->Frame->Card->Channel. Все они представлены в виде объектов, и между ними есть отношения Родитель-Ребенок. По сути, канал открыт только для карты, карта - только для кадра, а кадр - только для системы.
В идеале я хотел бы представить методы только из класса System для внешнего мира. Однако в классах Channel, Card и Frame есть важные события. В настоящее время я обращаюсь с ними через распространение. Предположим, что событие произошло в канале. Это событие сначала вызывается в Карте, затем в Кадре, а затем, наконец, в Системе. Вы можете видеть, как это приводит к большому количеству кода. Но моя главная проблема не в коде, а в производительности.
Как вы думаете, это распространение плохо влияет на мою производительность? Есть ли способ сделать его более эффективным? Какие еще варианты у меня есть? Мои коллекции относительно маленькие. Система 1, кадры < 16, карты < 256, каналы < 8192. Большая часть данных хранится в классе Channel, в котором есть только примитивные объекты.
редактирует
Вот код, который у меня есть в Карте для события, которое создается Каналом:
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 ответа
Единственный способ ответить, влияет ли это на вашу производительность или нет, состоит в том, чтобы протестировать ее: попробуйте один способ, где вы распространяете события, а затем другой, где вы напрямую подключаетесь. Смотрите, что быстрее.
При этом я не могу себе представить, что вы обнаружите значительное, если вообще есть, ощутимое влияние на производительность, когда у вас будет несколько последовательных вызовов делегатов вместо одного. Да, делегаты вызывают немного медленнее, чем реальные методы, поэтому, при прочих равных условиях, добавление большего количества уровней косвенности оказывает большее влияние, чем тот же уровень при обычных вызовах методов, но это пахнет преждевременной оптимизацией.
Ваш подход - это то, что я бы порекомендовал, если бы кто-то спросил, как делать то, что вам нужно. Иди с этим, если это не проблема.
РЕДАКТИРОВАТЬ
В ответ на ваш комментарий к другому ответу я хотел расширить. События 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, и связанный объект не изменится
- Предоставление ссылки на дочерний объект (как
object
) приемлемо
Если у вас несколько детей, это технически возможно (вы можете просто просмотреть их и прикрепить к ним в add
и удалить их все в remove
), но гораздо сложнее в управлении. Если связанный объект или коллекция объектов могут измениться после того, как событие присоединено, тогда становится практически невозможно координировать.
Кроме того, если ваши события следуют рекомендуемым методам для событий (где вы передаете запускающий объект как sender
), тогда, поскольку вы присоединяете цель непосредственно к дочернему событию, а не сами вызываете ее, вы будете выставлять ссылку на дочерний объект через этот аргумент. Я понятия не имею, является ли это уместным или приемлемым для вас, но это то, что следует рассмотреть.
Вы можете сделать это в классе System:
event EventHandler FrameEvent
{
add { this.frame.FrameEvent += value; }
remove { this.frame.FrameEvent -= value; }
}
Так что, если клиент делает это
system.FrameEvent += Handler;
Обработчик действительно привязан к событию в классе Frame.