Каковы размеры данных для событий и подписки на события?

Извиняюсь за то, что не успел примерить sizeof на мероприятии, но в духе усиления Google-фу....

  1. Какой фактический объем памяти будет добавлен к экземпляру класса, который имеет поле события?

  2. Какая фактическая память будет использоваться для каждой подписки на событие?

Я предполагаю отдельные указатели для каждого события или подписки, но я просто хочу быть уверен.

Рассмотрим этот пример:

public class VertexMovedArgs : EventArgs {
    public Vertex theVert;
}

public class Vertex {
    // what size does this add to each Vertex?
    public event EventHandler<VertexMovedArgs> VertexMovedEvent;
    private Vector3 m_pos;
    public Vector3 pos {
        get { return m_pos; }
        set { 
            if ( value != m_pos ) {
                m_pos = value;
                EventHandler<VertexMovedArgs> tellEveryoneWeMoved = VertexMovedEvent;
                if ( handler != null ) {
                     VertexMovedArgs args = new VertexMovedArgs( this );
                     tellEveryoneWeMoved( this, args );
                }
            }
        }
    }
}

public class Mesh {
    private List<Vertex> m_verts = new List<Vertex>();
    public Vertex AddVert() {
        Vertex vert = new Vertex();
        // what size per-subscription does this add to this Mesh instance (or elsewhere)?
        vert.VertexMovedEvent += onVertexMoved;
        m_verts.Add( vert );
    }
    void onVertexMoved( object sender, VertexMovedArgs args ) {
         // play the vertex like a violin, etc...
    }
}

2 ответа

Решение

Поле события - это просто ссылка на объект.
Пока вы не поместите обработчик (делегат) в него, он будет просто потреблять один размер указателя (4 или 8 байт) на экземпляр класса.

Экземпляр делегата имеет четыре поля размера указателя плюс стандартный заголовок объекта CLR.

Они хранят:

  1. Цель для вызова закрытого делегата
  2. MethodBase функции, указанной на
  3. Указатель на функцию для вызова
  4. Вспомогательный указатель, хранящий адрес фактической указанной функции (используется с открытыми делегатами для перестановки параметров)

Многоадресные делегаты (на практике все обычные делегаты) добавляют еще два:

  1. Массив делегатов, на которые указывает многоадресный делегат (null для одиноких делегатов)
  2. _invocationCount (IntPtr), используется по-разному для разных типов делегатов

Я нахожусь в процессе написания поста в блоге, который объяснит эти поля более подробно.

Событие - это пара методов для добавления и удаления подписок (на самом деле это три метода, но третий метод вообще ни для чего не используется). Сами события ничего не добавляют к размеру экземпляра объекта, но логика добавления / удаления обычно должна добавлять хотя бы одно поле. Наиболее распространенной реализацией события по умолчанию является создание поля типа MultiCastDelegate и использование Delegate.Combine для добавления подписок и Delegate.Remove для их удаления.

Другие вопросы по тегам