2014-10-17 2 views
4

Так что я сделал несколько интерфейсов, как это:Как я могу работать с явными событиями интерфейса?

public interface IDrawActions : ISimpleDrawable 
{ 
    Action<GameTime> PreDrawAction { get; set; } 
    Action<GameTime> PostDrawAction { get; set; } 

    event EventHandler PreDrawActionChanged; 
    event EventHandler PostDrawActionChanged; 
} 

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

An explicit interface implementation of an event must use event accessor syntax

И погуглить это привело меня к this rather helpful blog post:

This hints at one of the primary reasons for writing your own add and remove accessors: to provide your own underlying data store. One reason you might want to do this is if you have lots of exposed events on your class, but in such a way that only a few are typically in use on an instance at any point in time. In such a scenario, there can be significant memory overhead associated with maintaining a delegate field for each event.

Как именно это сэкономить на ресурсах? Кажется, что список вызовов делегатов для события будет нулевым, но как и когда он будет фактически создан, если вы используете свои собственные пользовательские обработчики? Все скрыто!

+0

http://stackoverflow.com/questions/2268065/c-sharp-language-design-explicit-interface-implementation- of-an-event – mybirthname

+0

Я понимаю, как работать с явным синтаксисом событий, но это был не мой вопрос; Я просил разъяснения о выделенном тексте, где он утверждает, что существует «значительная нехватка памяти», –

ответ

1

Текст, выделенный полужирным шрифтом, относится к оптимизации памяти, которая чрезвычайно полезна, когда у вас много событий или много объектов, все из которых имеют множество событий. Самая основная поддержка для создания событий в C# заключается в использовании ключевого слова event. Это ключевое слово является синтаксическим сахаром для следующего сгенерированного кода:

  • Поле, содержащее делегат. Делегаты формируют связанные списки. Это глава списка, и дополнения вставляются во главе.
  • Агенты доступа, в которые метод добавления добавляется в связанный список, используя поле делегата, и метод удаления удаляется из связанного списка. У скрепленного добавления и удаления списка также есть синтаксический сахар, скрывающий его, поэтому вы видите только «+ =» и «- =» для добавления или удаления из списка делегатов.

В этом смысле события ключевого слова создает код, похожий на сгенерированный код из C# авто Реализуемых свойств.

Накладные расходы при сохранении отдельного поля для каждого события. Это необязательно, так как нет необходимости поддерживать отдельное поле для данных, поддерживающих каждое свойство, открытое классом. Мы можем виртуализировать как поля событий, так и поля свойств.

Как устранить накладные расходы для событий конкретно? Мы используем этот метод в таких библиотеках, как VG.net, и Microsoft использует аналогичные методы в своем коде: сохраняйте коллекцию событий в одном поле. В большинстве случаев несколько экземпляров имеют множество подписчиков событий. Простейшая коллекция - это связанный список экземпляров класса. Каждый элемент в коллекции состоит из экземпляра класса, содержащего следующие свойства:

  • Идентификатор события. Для уникального типа события есть один уникальный идентификатор. Лучше всего использовать что-то маленькое, например, байт или целое число, поскольку у вас вряд ли будут миллионы типов событий, даже в огромной библиотеке.
  • Делегат. Делегат может быть слабо типизирован (делегат).

Когда вам нужно добавить обработчик событий для подписчика, вы просматриваете делегат в своей коллекции, используя уникальный идентификатор типа события. В первый раз, когда вы его просмотрите, коллекция не будет содержать его. В случае добавления обработчика событий вы добавите элемент в свою коллекцию, и внутри этого элемента добавьте туда делегированный делегат, используя Delegate.Combine. Чтобы удалить обработчик, вы используете Delegate.Remove.

Вот пример из реального кода в VG.net:

private static readonly int MouseDownEvent = EventsProperty.CreateEventKey(); 

    public event ElementMouseEventHandler MouseDown 
    { 
     add { AddHandler(MouseDownEvent, value); } 
     remove { RemoveHandler(MouseDownEvent, value); } 
    } 

    public virtual void OnMouseDown(ElementMouseEventArgs args) 
    { 
     ElementMouseEventHandler handler = 
      FindHandler(MouseDownEvent) as ElementMouseEventHandler; 
     if (handler != null) 
      handler(this, args); 
    } 

    internal void AddHandler(int key, Delegate value) 
    { 
     EventsProperty p = (EventsProperty)GetOrInsertProperty(EventsProperty.Key); 
     p.AddHandler(key, value); 
    } 

    internal void RemoveHandler(int key, Delegate value) 
    { 
     EventsProperty p = (EventsProperty)GetProperty(EventsProperty.Key); 
     if (p == null) 
      return; 
     p.RemoveHandler(key, value); 
    } 

    internal Delegate FindHandler(int key) 
    { 
     EventsProperty p = (EventsProperty)GetProperty(EventsProperty.Key); 
     if (p == null) 
      return null; 
     return p[key]; 
    } 

Мы виртуализировать не только события, но и свойства, а также. Для VG.net все события содержатся в одном виртуальном свойстве (EventProperty), а большинство общедоступных свойств также виртуализованы, хотя мы объединяем значения свойств, которые наиболее вероятно используются вместе. Это позволяет нам предоставлять множество свойств и событий для всех экземпляров, в то время как для каждого экземпляра используется нулевая память, если:

  • Для свойств свойство имеет значение, отличное от значения по умолчанию.
  • Для событий что-то подписывается на мероприятие.

Эти типы оптимизации делают VG.net эффективным даже при наличии миллионов графических объектов в памяти, даже если они работают на низкопроизводительных аппаратных средствах.

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

Смежные вопросы