Делегаты и события
От: Алексей Дубовцев Россия  
Дата: 29.10.04 15:26
Оценка: 1393 (39)
Статья:
Делегаты и события
Автор(ы): Алексей Дубовцев
Дата: 26.03.2005
Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”, посвященная описанию работы с делегатами в .NET. Подробно разбираются многие аспекты работы с делегатами и на наглядных примерах раскрываются многие особенности использовании этой возможности.



Авторы:
Алексей Дубовцев

Аннотация:
Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”, посвященная описанию работы с делегатами в .NET. Подробно разбираются многие аспекты работы с делегатами и на наглядных примерах раскрываются многие особенности использовании этой возможности.
Re: Делегаты и события
От: akasoft Россия  
Дата: 11.11.04 16:14
Оценка:
Здравствуйте, Алексей Дубовцев, Вы писали:

АД>Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”


Прочитал в журнале статью. Освоил делегаты. И что, вся книга такая? Тогда искать-искать...
... << RSDN@Home 1.1.4 beta 3 rev. 230 silent >>
Re: Делегаты и события
От: Daenur Россия  
Дата: 13.12.04 22:36
Оценка:
Купил книгу. Понравилась, хотя только начал читать.
Советую выложить описание в ресурсах.
(_2B || !_2B)
Re: Делегаты и события
От: Аноним  
Дата: 14.12.04 05:37
Оценка:
Тута усё ема: http://www.gotdotnet.ru/Forums/SiteDiscuss/92730.aspx

А вначале, когда ничего не было, всё было так...
Ставьте не спасибо, а рейтинг.


данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Re: Делегаты и события
От: beroal Украина  
Дата: 28.03.05 07:08
Оценка:
АД>Статья:
АД>Делегаты и события
Автор(ы): Алексей Дубовцев
Дата: 26.03.2005
Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”, посвященная описанию работы с делегатами в .NET. Подробно разбираются многие аспекты работы с делегатами и на наглядных примерах раскрываются многие особенности использовании этой возможности.


Использование делегатов и событий в обычных управляемых приложениях несложно.

Это, видимо, у автора юмор такой. Ну и накручено с делегатами, особенно add и remove.
Делегаты и события
От: Аноним  
Дата: 28.03.05 09:11
Оценка:
>?, 29.10.04 20:26
>Делегаты и события

Это хорошо, что у нас появляются подобные книги. Но прочитал статью и осталось чувство некоторой логической незавершенности изложенного материала. dot Net не был бы серьезной технологией, если бы не позволял запускать методы сервиса на другом сетевом компьютере, и передавать в качестве параметра функцию обратного вызова. Как должен быть оформлен этот вариант? Можно ли из WebService вызвать удаленный метод remoting сервиса с обратным вызовом? и т.п. Много вопросов по применению делегатов в сети. Надо бы автору на них ответить.


данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Делегаты и события
От: Аноним  
Дата: 28.03.05 10:21
Оценка:
Реклама собственной книги?
[tagline]
Все должно быть просто, очень просто, настолько просто — насколько это возможно!
<hr />данное сообщение получено с www.gotdotnet.ru<br />
ссылка на [url=http://www.gotdotnet.ru/forums/CommonForum/138168.aspx]оригинальное сообщение
Re: Делегаты и события
От: Spaider Верблюд  
Дата: 28.03.05 10:53
Оценка: 65 (9) +1
Рассказывая про делегаты, автор обошел вниманием один существенный момент, на котором рано или поздно спотыкается большинство людей, пишущих на .NET:

Цитирую автора:

Существует два недостатка механизма вызова функций, связанных с делегатом. И оба они связаны со случаем, когда в списке вызова делегата присутствует более одной функции.


Так вот, проблем на самом деле больше Насколько, сказать не могу, но третью назову: время вызова Multicast Delegate'а растет в геометрической прогрессии от количества делегатов в списке. Т.е. это совершенно незаметно для списка порядка нескольких десятков делегатов, но уже при нескольких тысячах вызов всех делегатов может занять секунды, десятки секунд. Происходит это потому, что при вызове очередного делегата из списка, его поиск ведется заново от начала внутренного массива. Всех любопытных отсылаю к файлу \sscli\clr\src\vm\i386\cgenx86.cpp (выделено вашим скромным слугой):

//===========================================================================
// Emits code for MulticastDelegate.Invoke()
VOID StubLinkerCPU::EmitMulticastInvoke(UINT numStackBytes, BOOL fSingleCast)
{
  ...
  EmitLabel(pInvokeRecurseLabel);
    
  // This is disgusting. We can't use the current delegate pointer itself
  // as the recursion variable because gc can move it during the recursive call.
  // So we use the index itself and walk down the list from the promoted
  // head pointer each time.
   
  // mov SCRATCHREG, [esi + this]     ;; get head of list delegate
  X86EmitIndexRegLoad(SCRATCH_REGISTER_X86REG, kESI, MulticastFrame::GetOffsetOfThis());
  ...
}


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

И еще одно замечание по поводу

Листинг 10. Надежный способ работы с делегатами, ссылающимися на несколько методов.


Строго говоря, данный способ не является надежным. Дело в том, что список вызовов может измениться в процессе его обхода. Не важно, что будет тому причиной: добавление/удаление делегата из другого потока или "убиение" делегата сборщиком мусора. Следствием будет трудноуловимая спорадически появляющаяся ошибка.
Для полной надежности этому методу не хвататет синхронизации. Необходимо это отметить! Без этого замечание уважаемого автора о построении надежных систем выглядит, кхм, лёгкой иронией.
--
К вашим услугам,
Re[2]: Делегаты и события
От: Дед Пихто  
Дата: 29.03.05 12:36
Оценка:
Здравствуйте, Spaider, Вы писали:

С интересом и благодарностью прочитал Ваши справедливые замечания к статье. Но также хочется выразить свою поддержку и автору статьи. Imho, возможная низкая производительность "обхода" списка делегатов не является таким же "недостатком" как и в том смысле, как их сгрупировал автор. Это скорее ограничение технологии, а не классический недостаток. Похожим способом реализован также обход списка в COM для connection points. Да и "делегаты" там хранятся в массиве (vector). Что касается проблемы обхода списка с помощью итератора и возможным отстутсвием обработчика, то ведь если список функций изменится в процессе его обхода, то этот факт должен отобразить итератор. Imho, приведенный Вами комментарий к методу MulticastDelegate.Invoke() касается разработчиков, поддерживающих код в файле sscli\clr\src\vm\i386\cgenx86.cpp. Чтобы они учитывали приведенную Вами ситуацию. Не задача пользователя заботиться об возможном изменении списка.

S>Для полной надежности этому методу не хвататет синхронизации. Необходимо это отметить! Без этого замечание уважаемого автора о построении надежных систем выглядит, кхм, лёгкой иронией.


Правильно, но она нужна прежде всего методу MulticastDelegate.Invoke(), а не пользовательскому коду.


P.S. Я без наездов.
Re[3]: Делегаты и события
От: Spaider Верблюд  
Дата: 30.03.05 07:20
Оценка:
Здравствуйте, Дед Пихто, Вы писали:

ДП>С интересом и благодарностью прочитал Ваши справедливые замечания к статье. Но также хочется выразить свою поддержку и автору статьи.


А никто и не умаляет заслуг автора. Грамотная статья, своих 60 баллов я влепил без сожаления

ДП>Imho, возможная низкая производительность "обхода" списка делегатов не является таким же "недостатком" как и в том смысле, как их сгрупировал автор. Это скорее ограничение технологии, а не классический недостаток.


Согласен, но это действительно становится проблемой, когда реализанция классов завершена, релиз на носу, а при тестировании на реальных данных оказывается, что граф объектов десереализуется минут 10-20. Я не фантазирую, пример из нашей жизни, Plutonia Experiment подтвердит.

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


В том случаею, если итератор потокобезопасен. А вдруг нет

ДП>Imho, приведенный Вами комментарий к методу MulticastDelegate.Invoke() касается разработчиков, поддерживающих код в файле sscli\clr\src\vm\i386\cgenx86.cpp.

ДП> Чтобы они учитывали приведенную Вами ситуацию. Не задача пользователя заботиться об возможном изменении списка.

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

Разработчики .NET ведь не сделали обход списка делегатов по порядку, и почему не сделали, написали в коментариях. Другими словами, там написано, что обход списка делегатов итератором не является абсолютно безопасным. На уровне framework'а они не могли себе позволить допушение, которое мы позволяем себе на уровне приложения -- что список делегатов останется неизменен.

S>>Для полной надежности этому методу не хвататет синхронизации. Необходимо это отметить! Без этого замечание уважаемого автора о построении надежных систем выглядит, кхм, лёгкой иронией.


ДП>Правильно, но она нужна прежде всего методу MulticastDelegate.Invoke(), а не пользовательскому коду.


Ну, типа того.

ДП>P.S. Я без наездов.


Да ни боже мой...
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
--
К вашим услугам,
Re[4]: Делегаты и события
От: MrOrbit Россия  
Дата: 06.04.05 11:35
Оценка:
Здравствуйте, Spaider, Вы писали:


ДП>>С интересом и благодарностью прочитал Ваши справедливые замечания к статье. Но также хочется выразить свою поддержку и автору статьи.


S>А никто и не умаляет заслуг автора. Грамотная статья, своих 60 баллов я влепил без сожаления


Всем спасибо, кто прочетал статью и даже влепил за неё баллы

ДП>>Imho, возможная низкая производительность "обхода" списка делегатов не является таким же "недостатком" как и в том смысле, как их сгрупировал автор. Это скорее ограничение технологии, а не классический недостаток.



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


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


Ещё раз всем больше спасибо.
Re: Делегаты и события
От: Аноним  
Дата: 06.04.05 11:59
Оценка:
Здравствуйте, Алексей Дубовцев, Вы писали:

Очепятка в статье (редакция 30.03.2005)

Листинг 14. Поддержка событий в ручном режиме.

public void SimulateClick()
{
// Вызываем функции, связанные с событием Click,
// предварительно проверив, зарегистрировался
// ли кто-нибудь в этом событии.
if (Click != null)
Click();
}



Click и Click() надо заменить на
_click и _click()
Re[2]: Делегаты и события
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.04.05 14:41
Оценка: 31 (4)
Здравствуйте, Spaider, Вы писали:

S>Так вот, проблем на самом деле больше Насколько, сказать не могу, но третью назову: время вызова Multicast Delegate'а растет в геометрической прогрессии от количества делегатов в списке. Т.е. это совершенно незаметно для списка порядка нескольких десятков делегатов, но уже при нескольких тысячах вызов всех делегатов может занять секунды, десятки секунд. Происходит это потому, что при вызове очередного делегата из списка, его поиск ведется заново от начала внутренного массива. Всех любопытных отсылаю к файлу \sscli\clr\src\vm\i386\cgenx86.cpp (выделено вашим скромным слугой):


Был такой баг. Похоже устранен в 2.0. Вот такой код:
using System;

class Program
{
    delegate void Test();

    static int _counter = 0;

    static void TestFunc()
    {
        _counter++;
    }

    static void Main(string[] args)
    {
        Test test = null;

        for (int i = 0; i < 100000; i++)
        {
            test += TestFunc;
        }

        test();

        Console.WriteLine(_counter);
    }
}

Выполняется почти молниеносно. На миллионе правда подтормаживает, но это уже определяется скоростью вызова делегата.

S>

Листинг 10. Надежный способ работы с делегатами, ссылающимися на несколько методов.


S>Строго говоря, данный способ не является надежным. Дело в том, что список вызовов может измениться в процессе его обхода. Не важно, что будет тому причиной: добавление/удаление делегата из другого потока или "убиение" делегата сборщиком мусора. Следствием будет трудноуловимая спорадически появляющаяся ошибка.

S>Для полной надежности этому методу не хвататет синхронизации. Необходимо это отметить! Без этого замечание уважаемого автора о построении надежных систем выглядит, кхм, лёгкой иронией.


Вот тут ты ошибашся. Делгаты immutable-объкты (не изменяемые). Вызов:
            test += TestFunc;

порождает новый делегат. По сути об этом сказано в статье:

Конкатенирует два (или более) делегата, создавая новый делегат, список вызовов которого включает списки объединяемых делегатов. Исходные делегаты не модифицируются.

Так что после вызвоа у нас в получается потокобезопастный список ссылок на методы, и никакая синхронизация не требуется.

Так что необходимо отметить, что фирма веников не вяжет.
... << RSDN@Home 1.1.4 beta 4 rev. 351>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Делегаты и события
От: AlexeyPr  
Дата: 08.06.05 05:03
Оценка: +1
Здравствуйте, Алексей Дубовцев

Заранее приношу свои извинения за свое ворчание...

Читал-читал статью и все время меня не оставляло ощущение "дежа вю". Наконец, вспомнил, где все это я уже видел — у старины Рихтера.

Алексей, если у Вас есть планы по написанию второй редакции книги, включите, плз, в главу информацию про поддержку компиляторами асинхронных вызовов делегатов: я про генерируемые компилятором методы
BeginInvoke(...);
EndInvoke(...);


ИМХО, читателям было бы очень полезно познакомиться с внутренним механизмом работы асинхронных вызовов делегатов в .NET. Тем более, что у Рихтера в книге этого нет...

PS: Еще раз сорри, если чем-то обидел.

С уважением,
Алексей.
Re: Делегаты и события
От: Smarty Россия  
Дата: 08.06.05 16:04
Оценка: -2
Здравствуйте, Алексей Дубовцев, Вы писали:

АД>Статья:

АД>Делегаты и события
Автор(ы): Алексей Дубовцев
Дата: 26.03.2005
Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”, посвященная описанию работы с делегатами в .NET. Подробно разбираются многие аспекты работы с делегатами и на наглядных примерах раскрываются многие особенности использовании этой возможности.



Цитата:

При обращении к событию, имеющему тип данного делегата, в качестве параметра необходимо передать ссылку на текущий объект (this), а во втором параметре экземпляр класса EventArgs или производного от него.
SomeEvent(this, EventArgs.Empty);
где свойство Empty попросту возвращает пустой экземпляр типа EventArgs. В принципе, его можно создать и самому воспользовавшись оператором new.
SomeEvent(this, new EventArgs());
Правда, такой подход будет работать несколько медленнее, чем первый, поскольку здесь появилась дополнительная операция создания объекта. И причем необходимо отметить, что она далеко из не самых быстродействующих.

Не всегда верно. Т.е. далеко не всегда. При обращении к EventArgs.Empty мы юзаем статик-конструктор:
static EventArgs()
{
EventArgs.Empty = new EventArgs();
}
а при обращении к new EventArgs() еще и инстанс-конструктор, код которого, не сказать что б слишком сложен:
public EventArgs()
{
}

И обратите внимание, что класс EventArgs не содержит инстанс-полей, т.е. под новый объект этого типа новая память НЕ нужна.
Re[2]: Делегаты и события
От: TK Лес кывт.рф
Дата: 08.06.05 16:21
Оценка:
Hello, "Smarty"
> И обратите внимание, что класс EventArgs не содержит инстанс-полей,
> т.е. под новый объект этого типа новая память НЕ нужна.

Странные у тебя расклады... Объект есть, а память не нужна?
Posted via RSDN NNTP Server 1.9
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[2]: Делегаты и события
От: arh Россия  
Дата: 08.06.05 16:34
Оценка:
S>Не всегда верно. Т.е. далеко не всегда. При обращении к EventArgs.Empty мы юзаем статик-конструктор:
S>static EventArgs()
S>{
S> EventArgs.Empty = new EventArgs();
S>}
Этот код выполняется всего один раз при инициализации типа.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Все изложенное является моим личным мнением на текущий момент.
Re: Делегаты и события
От: mihailik Украина  
Дата: 09.06.05 07:01
Оценка: -2
АД>Глава из книги Алексея Дубовцева “Microsoft .NET в подлиннике”

(1) Делегаты являются ссылками на методы, инкапсулирующими настоящие указатели и предоставляющими удобные сервисы для работы с ними.

(2) Ссылки представляют собой объекты соответствующего типа.

(3) Все делегаты являются объектами типа System.Delegate или System.MulticastDelegate, который является производным от первого.


Стиль трудночитаемый.
(1) Два деепричастных оборота, оба осложнены зависимыми словами. Нужно думать, чтобы понять. Кроме того, ссылки "предоставляют сервисы для работы с ними" — саморекурсия, в которой без поллитры не разберёшься.

(2) Предложение вообще не несёт смысловой нагрузки.

(3) К чему относится слово "который"? Формально предложение неверно построено. Неформально — понять можно, но только перебором версий.


Словом, от текста есть ощущение сонливой вузовской лекции. Желаю автору серьёзно работать и избавляться от этих наукообразных длиннот. Лучший способ — резать.

Кстати, в журнале RSDN была толковая статья "как не надо писать статьи".
Re[2]: Делегаты и события
От: Mika Soukhov Stock#
Дата: 09.06.05 11:53
Оценка: :))
Здравствуйте, mihailik, Вы писали:

Редактор схалявил, а статья нормальная.
Re[2]: Делегаты и события
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.06.05 01:04
Оценка:
Здравствуйте, Smarty, Вы писали:

S>Не всегда верно. Т.е. далеко не всегда. При обращении к EventArgs.Empty мы юзаем статик-конструктор:

S>static EventArgs()
S>{
S> EventArgs.Empty = new EventArgs();
S>}
S>а при обращении к new EventArgs() еще и инстанс-конструктор, код которого, не сказать что б слишком сложен:
S>public EventArgs()
S>{
S>}
S>

1. Статический конструктор вызывается при первом обращении к типу. Его даже создавать не нужно. Любая попытка обратиться е его членам приведет к его вызову. В том числе и попытка создать экземпляр этого типат (обратиться к конструктору).
2. Статический конструктор вызывается ровно один раз. Так что времени он практически не отнимает.

S>И обратите внимание, что класс EventArgs не содержит инстанс-полей, т.е. под новый объект этого типа новая память НЕ нужна.


3. Любой объект созданный в куче занимает как минимум 8 байт (для 32-битной платформы). 4 байта занимает ссылка на описание тиа и еще 4 специальное поле для хранения индекса объекта синхронизации. К тому же дело не в памяти. 8 байт — это ничто по сравнению с 8 метрами которые дотнет отводит по умолчанию для процесса. Их никто и не заметит. Но создание объекта — это еще куча действий такик как выделение памяти в куче, инициализация объекта (тех самых полей) и т.п. Операция эта довольно быстра но все же значительно медленее чем копирование ссылки.
4. Обращение к EventArgs.Empty — это всего лишь копирование ссылки. Быстрее нее может быть только копирование вэлью-типа небольшого размера.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.