Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

Archive for the ‘Events’ Category

Отзыв о конференции DotNext Piter 2018

8 комментариев

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

Первое что скажу — 12 спонсоров в этот раз, это круто, вроде в 1й присест был JetBrains один и то бесплатно, а тут 12. Правда спонсоры это в основном сервисные компании, которые платят копейки (копейки в моем понимании это <$1kk/год) — EPAM, DataArt (черт, я там работал целых аж 3 месяца), и прочие подобные конторы. Из продуктовых там были JetBrains, Fast Reports, хз не помню кто еще. Главным спонсором был Альфабанк, для меня фраза “ритейловый банк” синоним “раковая опухоль”, да не будем о плохом, товарищи. Короче я понял что я unemployable :)

Так вот, посетил я не все доклады, а только некоторые, так что вот мои отзывы:

Apps, algorithms and abstractions: decoding our digital world, Dylan Beattie: давайте начнем с того что SkillsMatter это какбы контора которая, как и я, зарабатывает деньги. Что они там делают — ну, тренинги делают. А сам по себе доклад был 100% популистский, что хорошо, т.к. конфа должна начинаться и заканчиваться чем-то легким. Поэтому было вообще ОК, Дилан рассказал про то сколько всего сложного происходит в тот момент когда ты получаешь на мобильнике картинку котика и пишешь LOL. Отдельный ему респект за русскоязычный юмор (щи да каша пища наша!), очень хорошо подготовился. Почему правда англичанин одевается как техасский ковбой история умалчивает, наверное это часть его рок-имиджа — это ведь он (и Вагиф Абилов, кстати!) играли рок на закрытии первого дня. Годно.

Fastware, Andrei Alexandrescu, The D Language Foundation — эээ, D какбы мертв, а Алексадреску обычно делает доклады на С++ конференциях вроде ACCU, C++Now и прочие, т.к. он легенда С++ мира который ушел в D. Который не взлетел — он хоть и делал там что-то на D в фейсбуке, но в отличии от Rust нехватило момента и язык не смог преодолеть силу тяготелния. Доклад Александреску показался мне немного несобранным и лишенным консистентности — он конечно потрудился чтобы перевести свои примеры в C#, но примеров было мало и они были клинично-прецезионными. Скорость — это не про те языки где нельзя напрямую писать ASM или манипулировать SIMDом, хотя что-то на C# у вас определенно получится. В целом, доклад не особо понравился.

Lightweight microservice collaboration using HTTP, Christian Horsdal — типичный консалтерский доклад высокого уровня. Я еще должен подчеркнуть тут, что microservices are not a thing: коммуникация через веб-интерфейсы это хорошо, но уровень дробления должно быть адекватно, и резать все на “микро” это очень тупо и никому нафиг не нужно. Поэтому просмотрел доклад, не почерпнув ни одной особой мысли. Да, в микросервисах появляются задачи вроде “как описать весь API”, “что нужно делать при поломке”, но эти задачи не отличаются от аналогов с работос с очередями вроде MSMQ, RabbitMQ и всем этим остальным адом. CQRS/ES — это круто и нужно. Микросервисы — скорее все же нет.

Взаимодействие микросервисов по HTTP/2, Евгений Жиров — вот этот доклад мне понравился, т.к. это была реальная case study о том как использовать HTTP/2. Докладчик объяснил зачем им это было надо, на какие грабли они наткнулись и про “а как же .NET Core”. Совершенно годный доклад, и хоть его основной вывод “нужно подождать”, мне понравилось.

Building real world production-ready web APIs with .NET Core, Alex Thissen — еще один доклад по микросервисам который тоже меня не впечатлил. Было чуть больше конкретики, у докладчика был реальный проект, но опять же весь рассказ как-то скомкался воедино, у него не было какой-то общей структуры верхнего уровня, и в результате я не могу сказать чтобы я из него что-то важное. Опять же, microservices aren’t a thing поэтому горевать по ним никто не будет.

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

Вот как-то так. В Москву я на DotNext не поеду — терпеть не могу этот город, и даже когда был евангелистом испытывал от него тошноту. И вообще, в этом году у меня было желание съездить на Build, но всем из РФ закрыли визы, а лететь одному — ну, в принципе реально, но как-то совсем одиноко. Как-нибудь в другой раз, когда санкции снимут. Хотя я почти уверен что их уже вообще не снимут. Ну еще несколько лет точно.

Да, похвастаюсь, у меня вышла книжка. Такие дела.

Реклама

Written by Dmitri

24 апреля 2018 at 22:06

Опубликовано в Events

Tagged with ,

Фотоотчет с конференции .NEXT

4 комментария

Я должен признаться, что я редко участвую в конференциях. Во встречах юзер-групп – да, бывает, но на конференции меня мало кто зовет (хотя доклады я подаю постоянно). Поэтому тот факт что я не только участвовал в конференции спикером но еще и принимал участие непосредственно в организации – это исключение из правил. А если учесть что конференция была в России — вообще курьез!

То что следует ниже – это небольшой фотоотчет о том что же собственно было, а также немного инфы о том кто на картинках. Основная программа пероприятия находится на http://dotnext.ru, но читать ее наверное не так интересно.

Итак, поехали.

Общая часть

Прежде всего, вот он – Алексей Федоров, человек без которого бы ничего этого не произошло. Алексей организует много конференций, в т.ч. Joker и JPoint, также именно он придумал логотип .NEXT (все варианты от дизайнеров были унылыми). Вообщем, respect!

Собственно сам киноут делал я :) рассказывал про то как эволюционируют процессы разработки, что нового и интересного, под конец поотвечал на вопросы зрителей, в очередной раз показал в деталях поддержку в R# интерфейса INotifyPropertyChanged :)

Далее в главном зале доклад делал Станислав Сидристый – рассказывал он про детали реализации CLR. Очень хардкорно, и насколько я знаю он теперь делает мероприятие в Москве, workshop как раз в продолжение этой темы.

Впрочем, меня там уже не было т.к. я в это время был у нашей «будки» вместе с еще одним соорганизатором конференции – Филиппом Торчинским:

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

Часть 1я

Тем временем, конференция разделилась на 2 трека. В большом зале делал доклад Сергей Шкредов, тим лид команды ReSharper. Доклад был про организацию системы зависимостей в большом продукте, а Решарпер по меркам индустрии о-о-очень большой продукт, 300+ проектов.

В малом зале в это время выступал Рома Здебский, обсуждались новости с недавно прошедшей конференции BUILD. Многие пришли к нам как раз за этим докладом. Ну и конечно после конференции Рома еще долго общался с посетителями в кулуарах.

Далее в главном зале Кирилл Скрыган, который тоже работает в команде ReSharper, рассказывал про оптимизацию приложений. Согласно рейтингам, доклад Кирилла был самый популярный на конференции, мне же в свою очередь осталось лишь довольствоваться 2-м местом.

В малом зале доклад делал Алексей Садомов. Алексей – в прошлом один из активных участников Петербургской Группы Alt.Net – приехал к нам из Финляндии. Рассказывал он про интеграцию ASP.NET MVC приложений с Yandex.Market.

На этом этапе был кофе-брейк…

Часть 2я

После перерыва, в главном зале Влад Чистяков, наш коллега из JetBrains Moscow, рассказывал про фреймворк Nitra.

В это же время, Юлия Фаст из компании М13 (тоже кстати из Москвы) рассказывала про автоматизацию приемочного тестирования с помощью Fitnesse и TeamCity.

Далее, в главном зале был еще один доклад по TeamCity, уже непостредственно от сотрудника команды TeamCity. Евгений Кошкин расскывал про feature branches и их роль в процессе непрерывной интеграции.

А в малом зале, доклад проводил… я! Ну, как вы знаете, на любом эвенте бывают косяки и в этом случае, докладчик Виталий Баум (кстати тоже один из основателей Петербургской Группы Alt.Net) не смог к нам приехать, поэтому доклад мы делали по Скайпу. Виталий рассказал про то, как их компания использует Windows Azure для автоматизации управления функционалом автомобилей, а я в это время играл роль «ведущего телемоста» :)

Далее, Роман Белов (тоже из JetBrains) рассказал про профилировщик памяти dotMemory.

…а Станислав Сидристый в это время сделал короткий доклад на тему использования Xamarin для кроссплатформенной разработки.

Ну и в заключение, Антон Оникийчук (еще один человек из нашей ALT.NET тусовки) рассказал про использование TDD для MVVM приложений…

…а Павел Цытович из Luxoft Training в это время рассказал про использование WF для построения WCF сервисов. Я правда почему-то думал что WF уже умер в силу невостребованности, а оказывается нет.

Заключение

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

Мысли о следующией .NET конференции уже витают в облаках, но я пока обещать ничего не могу в силу огромных геополитических рисков. Надо еще посмотреть, выключат ли VISA/Mastercard 1го Июля, введут ли выездные визы, отменят ли двойное гражданство? Кто знает, кто знает…

Да, и кстати, вот все видеозаписи с конференции:

Written by Dmitri

18 мая 2014 at 15:13

Опубликовано в Events

Tagged with

Анонс весенних мероприятий

19 комментариев

Несмотря на мои отчаянные попытки жить за пределами России, у меня на сегодняшний день запланированы как минимум 4 мероприятия, на которых я планирую делать доклады по ReSharper а также другим интересным технологиям. Вот предварительный список мест, где я планирую появиться:

  • HTML5 Camp, Москва, 15 Марта — буду делать доклад на тему того как ReSharper ускоряет разработку веб-приложений. Как всегда, не обойдется без обильной кодогенерации а также использования Zen Coding (или даже Emmet, если получится адаптировать к ITextControl).

  • Хельсинки, 3 Апреля — буду делать два доклада, один по ReSharper, другой по использованию F#.

  • Ижевск+Казань, 6-9 Апреля — в Ижевске я расскажу про то, как использовать ReSharper при разработке приложений для Windows Phone 8. Для казани программа еще не определена, но планируется несколько докладов, так что помимо ReSharper/dotX тулов я также возможно расскажу про такие вещи как построить типографический редактор на MPS или простую систему финансовой отчетности на основе YouTrack Workflow Editor (я не шучу!).

  • Рига, 18 Апреля — доклад по Fast Code Craft на встрече DevClub.lv, Рига, Латвия.

  • Санкт-Петербург, 25 Апреля — доклад по YouTrack Workflows на встрече Петербургского ДевКлуба. Встреча пройдет в технопарке Ингрия.

  • Microsoft DevCon, подмосковье, 29-30 Мая — тут планируется много всего. Во-первых, будет стэнд, это значит что у вас будет два дня на то чтобы позадавать каверзные вопросы про ReSharper и другие продукты. Во-вторых, будет короткий, 30-минутный доклад под названием «Что нового в ReSharper 8». Ну и наконец мы планируем круглый стол, чтобы пообщаться на тему наших продуктов и всего такого.

Официальные анонсы разных мероприятий будут появляться по мере их готовности. Мероприятия в России мы будем проводить вместе с Филиппом Торчинским (@philipto) — новым евангелистом JetBrains по России.

Written by Dmitri

3 марта 2013 at 21:27

Опубликовано в Events

Tagged with ,

Брокеры событий, часть 3

7 комментариев

Продолжая наше обсуждение брокеров событий, давайте посмотрим на то, как можно в статических сценариях (когда все подписки известны на момент компиляции) реализовать брокер через аттрибуты и расширение к используемому IoC-контейнеру. Внимание: это достаточно сложный пост на тему расширения Unity.

Выкладки брокера соответствуют аналогичным файлам из примеров в ObjectBuilder с той лишь поправкой что они переписаны для использования Unity 2.0. Примеры находятся тут: http://bitbucket.org/nesteruk/eventbrokers.

Собственно сам брокер

Начнем с простого, а именно с брокера. Реализация брокера подразумевает все те же два извечных участника – подписчик (event sink) и некто кто публикует события (event source). В реализации брокера для Unity, оба эти участника выделены в отдельные классы – это в какой-то мере отличается от нашей модели с Subscription-классом из предыдущего поста. Почему? Все просто – там у нас была связка по интерфейсу. Тут у нас вообще полный loose coupling – мы только знаем что публикующий класс содержит событие (да-да, event), а в “получающем” классе нам известен только метод который берет все те же (object, EventArgs) к которым все уже привыкли. ОК, давайте посмотрим на эти инфраструктурные классы (а таких тут будет еще уйма).

EventSource

Этот класс инкапсулирует информацию об источнике события – именно поэтому в конструкторе появился EventInfo. На основе информации о событии динамически формируется и добавляется подписка на это же событие. Также, по аналогии с нашим предыдущем примером, класс-источний помечен как IDisposable, и его метод Dispose() делает… угадайте что! Правильно – отписывает всех обработчиков от обработки этого события.

internal class EventSource : IDisposable
{
  readonly string eventID;
  readonly EventInfo eventInfo;
  readonly MethodInfo handlerMethod;
  readonly EventBroker broker;
  readonly WeakReference source;
  public EventSource(EventBroker broker,
                      object source,
                      EventInfo eventInfo,
                      string eventID)
  {
    this.broker = broker;
    this.source = new WeakReference(source);
    this.eventInfo = eventInfo;
    this.eventID = eventID;
    handlerMethod = GetType().GetMethod("SourceHandler");
    Delegate @delegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, handlerMethod);
    eventInfo.AddEventHandler(source, @delegate);
  }
  public object Source
  {
    get { return source.Target; }
  }
  public void Dispose()
  {
    object sourceObj = source.Target;
    if (sourceObj != null)
    {
      Delegate @delegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, handlerMethod);
      eventInfo.RemoveEventHandler(sourceObj, @delegate);
    }
  }
  public void SourceHandler(object sender,
                            EventArgs e)
  {
    broker.Fire(eventID, sender, e);
  }
}

EventSink

Этот класс инкаспулирует в себе “приемник” событий. Он делает две вещи – во-первых, он как бы “регистрирует” привязку конкретного события к методу-обработчику – поэтому в конструкторе у нас фигурирует MethodInfo. В конструкторе также формируется тип EventArgs которые в последствии будут передаваться в обработчик.

Вторая вещь, которой занят приемник – это формирование и вызов того делегата, который отвечает за обработку конкретного события.

internal class EventSink
{
  readonly Type handlerEventArgsType;
  readonly MethodInfo methodInfo;
  readonly WeakReference sink;
  public EventSink(object sink,
                    MethodInfo methodInfo)
  {
    this.sink = new WeakReference(sink);
    this.methodInfo = methodInfo;
    ParameterInfo[] parameters = methodInfo.GetParameters();
    if (parameters.Length != 2 || !typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType))
      throw new ArgumentException("Method does not appear to be a valid event handler", "methodInfo");
    handlerEventArgsType = typeof(EventHandler<>).MakeGenericType(parameters[1].ParameterType);
  }
  public object Sink
  {
    get { return sink.Target; }
  }
  public Exception Invoke(object sender,
                          EventArgs e)
  {
    object sinkObject = sink.Target;
    try
    {
      if (sinkObject != null)
      {
        Delegate @delegate = Delegate.CreateDelegate(handlerEventArgsType, sinkObject, methodInfo);
        @delegate.DynamicInvoke(sender, e);
      }
      return null;
    }
    catch (TargetInvocationException ex)
    {
      return ex.InnerException;
    }
  }
}

Коротко о самом брокере

Кода получается много, поэтому попробую быстренько описать что у нас фигурирует в самом брокере. Естественно, что у нас есть списки подписчиков и обработчиков.

readonly ListDictionary<string, EventSink> sinks = new ListDictionary<string, EventSink>();
readonly ListDictionary<string, EventSource> sources = new ListDictionary<string, EventSource>();

Код использует класс ListDictionary, который можно посмотреть в исходниках. В принципе, тут подошел бы и MultiDictionary который мы использовали ранее.

Итак, вот какие методы реализует брокер:

  • RegisterSource(), RegisterSink() — добавление обработчиков и публикаторов (это валидное слово?) в списки
  • UnregisterSource(), UnregisterSink() — соответственно их удаление
  • Fire() — вызывает все обработчики для события с конкретным именем
  • RemoveDeadSinksAndSources() — удаляет из списков все “мертвые” ссылки; надеюсь понятно почему в исходниках либерально фигурирует WeakReference
  • Dispose() — удаляет все источники

Коротко о расширении Unity

Наверное никого не удивит что контейнер Unity можно расширять, добавляя новый функционал. Чтобы сделать свое расширение, нужно просто отнаследовать от класса UnityContainerExtension и дальше добавить те стратегии (о стратегиях поговорим через секунду) которые реализует данное расширение. Вот пример с нашим брокером:

public class BrokerExtension : UnityContainerExtension
{
  private readonly EventBroker broker = new EventBroker();
  protected override void Initialize()
  {
    Context.Container.RegisterInstance(broker,
      new ExternallyControlledLifetimeManager());
    Context.Strategies.AddNew<BrokerReflectionStrategy>(
      UnityBuildStage.PreCreation);
    Context.Strategies.Add(new BrokerWireupStrategy(broker),
                            UnityBuildStage.Initialization);
  }
}

В данном примере, через переменную Context мы имеем доступ к собственно контейнеру (в который мы добавляем сам брокер – а могли бы сделать и RegisterType<>) а также к коллекции стратегий, которые будут применены. Для каждой стратегии описывается этап надстройки объекта, в котором эта стратегия задействована. У нас в примере две стратегии. Первая (BrokerReflectionStreategy) для рефлешна, с помощью которого мы узнаем кто что публикует или хочет слушать.

  public class BrokerReflectionStrategy : BuilderStrategy
{
  public override void PreBuildUp(IBuilderContext context)
  {
    Type typeToBuild = context.BuildKey.Type;
    
    if (typeToBuild != null)
    {
      var policy = new EventBrokerPolicy();
      RegisterSinks(policy, typeToBuild);
      RegisterSources(policy, typeToBuild);
      if (!policy.IsEmpty)
        context.Policies.Set<IEventBrokerPolicy>(policy, context.BuildKey);
    }
    base.PreBuildUp(context);
  }
  static void RegisterSinks(EventBrokerPolicy policy,
                            Type type)
  {
    foreach (MethodInfo method in type.GetMethods())
      foreach (SubscribesToAttribute attr in method.GetCustomAttributes(typeof(SubscribesToAttribute), true))
        policy.AddSink(method, attr.Name);
  }
  static void RegisterSources(EventBrokerPolicy policy, Type type)
  {
    foreach (EventInfo @event in type.GetEvents())
      foreach (PublishesAttribute attr in @event.GetCustomAttributes(typeof(PublishesAttribute), true))
        policy.AddSource(@event, attr.Name);
  }
}

Вторая – для того чтобы собственно зарегистрировать подписки (BrokerWireupStrategy).

public class BrokerWireupStrategy : BuilderStrategy
{
  private readonly EventBroker broker;
  public BrokerWireupStrategy(EventBroker broker)
  {
    this.broker = broker;
  }
  public override void PreBuildUp(IBuilderContext context)
  {
    var policy = context.Policies.Get<IEventBrokerPolicy>(context.BuildKey);
   
    if (policy != null && broker != null)
    {
      foreach (KeyValuePair<string, MethodInfo> kvp in policy.Sinks)
        broker.RegisterSink(context.Existing, kvp.Value, kvp.Key);
      foreach (KeyValuePair<string, EventInfo> kvp in policy.Sources)
        broker.RegisterSource(context.Existing, kvp.Value, kvp.Key);
    }
    base.PreBuildUp(context);
  }
}

Тут происходит, казалось бы, странная вещь – используются две стратегии вместо одной. Более того, фигурирует также policy-класс под названием EventBrokerPolicy который позволяет нам “перекинуть” данные из одной стратегии в другую. Одна из причин подобного – то, что в контексте стратегий не существует прямого выхода на контейнер. Иначе говоря, класс EventBrokerPolicy ниже – это некий DTO.

public class EventBrokerPolicy : IEventBrokerPolicy
{
  readonly Dictionary<string, MethodInfo> sinks = new Dictionary<string, MethodInfo>();
  readonly Dictionary<string, EventInfo> sources = new Dictionary<string, EventInfo>();
  public bool IsEmpty
  {
    get { return sinks.Count == 0 && sources.Count == 0; }
  }
  public IEnumerable<KeyValuePair<string, MethodInfo>> Sinks
  {
    get { return sinks; }
  }
  public IEnumerable<KeyValuePair<string, EventInfo>> Sources
  {
    get { return sources; }
  }
  public void AddSink(MethodInfo method,
                      string eventID)
  {
    sinks.Add(eventID, method);
  }
  public void AddSource(EventInfo @event,
                        string eventID)
  {
    sources.Add(eventID, @event);
  }
}

Как все это выглядит

Итак, Unity находит подписки и публикации по аттрибутам Publishes и SubscribesTo. В нашем примере мы снова использовали строковый идентификатор для описания собственно событий – тем самым, у нас получается какой-то подобие loose coupling в том смысле что абслолютно произвольный компонент, воткнутый в систему, может подписаться на определенный тип события только в том случае, если знает как это событие называется.

Ну да ладно, посмотрим же как все это используется. Во-первых, наши классы FootballPlayer и FootballCoach из предыдущих примеров упростились до минимума. Вот как выглядит игрок:

public class FootballPlayer
{
  [Publishes("score")]
  public event EventHandler PlayerScored;
  public string Name { get; set; }
  public void Score()
  {
    var ps = PlayerScored;
    if (ps != null)
      ps(this, new EventArgs());
  }
}

Естественно что в качестве eventArgs мог бы быть более сложный тип – мы уже видели как наше расширение Unity само определяет этот тип и строит по нему делегат. Что касается тренера, теперь он выглядит вот так:

public class FootballCoach
{
  [SubscribesTo("score")]
  public void PlayerScored(object sender, EventArgs args)
  {
    var p = sender as FootballPlayer;
    Console.Write("Well done, {0}!", p.Name);
  }
}

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

var uc = new UnityContainer();
uc.AddNewExtension<BrokerExtension>();
var p = uc.Resolve<FootballPlayer>();
p.Name = "Maradona";
var c = uc.Resolve<FootballCoach>();
p.Score();
p.Score();

Заключение

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

Получив декларативность мы потеряли ту экспрессивность что нам давали Reactive Extensions. К сожалению, совместить оба эти подхода достаточно сложно. Более того, мне кажется что они созданы для различных сценариев, и ничто не мешает подписчику самому сделать push-коллекцию из callback’ов и оперировать ей через Rx.

Вот собственно пока все. Comments welcome.

Written by Dmitri

7 июля 2010 at 9:06

Опубликовано в .NET, C#, Events

Брокеры событий, часть 1

5 комментариев

Вообще в сложных, динамических системах очень сложно угнаться за меняющейся организацией компонентов, и если мы еще кое-как (это в 21-м то веке!) разурлили проблему создания компонентов с помощью специализированных контейнеров, то взаимодействие из между собой нам все еще полностью не подвластно. Например, реагирование на события в .Net (да и в других языках наверное) сделано на каком-то уж очень несерьезном уровне. И естественным образом в этой задаче появляются всякие инфраструктурные решения, о которых мы и поговорим.

Задача

Для большинства разработчиков, особенно разработчиков которые в аутсорсах посиживают, реагирование на события (я имею ввиду в основном event-ы) либо не существует совсем, либо существует со страшной силой, но в очень предсказуемом ключе. Наприемер, когда кто-то нажимает на кнопочку и отрабатывается событие button_Click.

Как только с событиями нужно работать вплотную, игра тут же останавливается. Вот вам пример – берем футболиста и тренера:

public class FootballPlayer
{
  public string Name { get;set; }
  public void Score()
  {
    var e = Scored;
    if (e != null) e(this, new EventArgs());
  }
  public event EventHandler Scored; 
}
public class FootballCoach
{
  public void Watch(FootballPlayer p)
  {
    p.Scored += (_, __) =>
    {
      Console.WriteLine("I'm happy that {0} scored", p.Name);
    };
  }
}

В этом случае подписка и уведомление работают хорошо:

var p2 = c.Clone(); // deep copy :)
p.Score();

Весь прикол тут в том, что когда вы скопируете объект (будь то через MemberwiseClone() или deep copy с помощью BinaryFormatter), все подписки этого объекта будут потеряны.

Подписки можно, конечно, востановить руками или начать использовать вместо событий просто наборы делегатов или…. там, Expression<T>, что-то в этом роде. Но это только часть проблемы.

Следующая часть проблемы состоит в том, что в один прекрасный момент вы захотите чтобы ваши объекты подписывались на события автоматически. Например, вышел игрок на поле – тренер начинает за ним следить. С “отписками”, кстати, то же самое. Если все утрировать, получится примерно следующее:

class FootballCoach
{
  public FootballCoach(FootballPlayer[] players)
  {
    foreach (var p in players) {
      p.EntersField += new EventHandler(StartWatchingPlayer);
      p.LeavesField += new EventHandler(StopWatchingPlayer);
    }
  }
}

И так далее до посинения – в каждом StartXxx вы будете подписываться, в каждом EndXxx отписываться. Но и это еще не все.

Представьте теперь, что в системе таких объектов много. Все они посылают всем другим сообщения. Если делать пописки через +=, мы получим зверскую связанность и полную нетестируемость (а тестировать сообщения вообще сложно) нашего кода.

Ну и наконец надо и “о бедной конфигурации замолвить слово”. Ведь иногда хочется получать уведомления определенного типа вне зависимости от того, кто их послал. Например, судье толком все равно, кто на него матерится – игрок или тренер. (Это я так, символично.) А еще, не поверите, иногда хочется делать всякие хитрые преобразования вроде реагирования на события пакетами по 100 штук, раз в час, только в пятницу 13го в полнолуние, и т.п. К чему нынешнее положение дел не очень-то предрасположено.

Pub-sub

У среднестатистичного разработчика сразу появляется желание написать свой event broker. А что, почему бы и нет. Берем и пишем простой незамысловатый класс:

public class EventBroker
{
  private MultiDictionary<string, Delegate> subscriptions = 
    new MultiDictionary<string, Delegate>(true);
  public void Publish<T>(string name, object sender, T args)
  {
    foreach (var h in subscriptions[name])
      h.DynamicInvoke(sender, args);
  }
  public void Subscribe<T>(string name, Delegate handler)
  {
    subscriptions.Add(name, handler);
  }
}

Можно пошаманить над потокобезопасностью (я бы сюда инстинктивно воткнул ReaderWriterLockSlim) и т.п. но суть от этого не изменится. Получили мы брокер, который может заменеджить подписки на события. Конечно никакого QoS вы тут не получите, и всю логику связанную с выборками событий придется писать ручками, но уже есть некоторые подвижки – например, включив в качестве классификатора name, мы создали ситуацию в которой один класс может подписать обработчик на несколько эвентов одновременно.

Игрок больше не вывешивает события вообще.

public class FootballPlayer
{
  private readonly EventBroker broker;
  public string Name { get; set; }
  public FootballPlayer(EventBroker broker)
  {
    this.broker = broker;
  }
  public void Injured()
  {
    broker.Publish("LeavingField", this, new EventArgs());
  }
  public void SentOff()
  { // event args can be different for this one
    broker.Publish("LeavingField", this, new EventArgs());
  }
}

Теперь тренер подписывается через брокер:

public class FootballCoach
{
  private readonly EventBroker broker;
  public FootballCoach(EventBroker broker)
  {
    this.broker = broker;
  }
  public void Watch(FootballPlayer player)
  {
    broker.Subscribe<EventArgs>("LeavingField",
                                new EventHandler(PlayerIsLeavingField));
  }
  public void PlayerIsLeavingField(object sender, EventArgs args)
  {
    Console.WriteLine("Where are you going, {0}?",
                      (sender as FootballPlayer).Name);
  }
}

Здесь мы надеемся на полиморфность, связанную с тем что по канону все наследуют аргументы от EventArgs. Сильное типизирование тут не критично т.к. всегда можно делать приведение типов. Вот как все это будет выглядеть:

var uc = new UnityContainer();
uc.RegisterType<EventBroker>(
  new ContainerControlledLifetimeManager());
var p = uc.Resolve<FootballPlayer>();
p.Name = "Arshavin";
var c = uc.Resolve<FootballCoach>();
 
c.Watch(p);
 
p.Injured();
p.SentOff();

Что может сильно удивить так это то что события, фактически, были заменены на мессенджинг. Если вы как я много играетесь с NServiceBus и прочими системами, то это конечно вам покажется вполне естественной метаморфозой.

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

public class FootballCoach
{
  [SubscribesTo("PlayerIsLeaving")]
  public void PlayerIsLeavingField(object sender, EventArgs args)
  {
    Console.WriteLine("Where are you going, {0}?",
                      (sender as FootballPlayer).Name);
  }
}

На то как это делается можно будет посмотреть в следующей части этого поста, а для тех кому не терпися, советую посмотреть сюда. Информация наверное немного устарела, но сам принцип думаю понятен.

Промежуточное заключение

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

Вторая проблема, о которой мы кажется уже говорили, заключается в том, что описание какой-то логики в обработке событий сейчас ограничено одним критерием – строковым литералом который работает чем-то вроде “классификатора”. Ограничивать логику подобными методами – глупо, особенно в связи с присутствием такого мощного инструмента как LINQ. (Думаю, намек понят.)

Written by Dmitri

24 июня 2010 at 0:32

Опубликовано в .NET, C#, Events

События в апреле

leave a comment »

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

ПКСД

Завтра (7го числа, 19:00) пройдет очередная встреча Питерского Клуба Своего Дела. Клуб недавно переехал в ДеЛаРук за Казанским, атмосфера там весьма демократична, так что всем у кого свое дело (или кто только планирует) рекоммендую прийти. Чтобы получать новости о встречах клуба нужно подписаться на рассылку на сайте клуба. Еще раз подчеркну – все встречи бесплатные, без снобизма, атмосфера хорошая. Для начинающих предпринимателей – самое то.

Sun TechDays

8го и 9го Апреля пройдут дни разработчика Sun (самой компании уже нет) где будет много рассказов о продуктах уже Oracle. Я был на СанТехе в прошлом году – был интересно но очень тесно. Совет тем кто идет в первый раз – ищите лоджии в главном зале – о них никто не знает, и там менее душно.

Запуск Visual Studio 2010

14го Апреля в Парк Инн Прибалтийская пройдет встреча посвященная запуску VS2010. Сама студия будет скорее всего доступна с 12го (запуск в Москве), так что в Питере наверное у всех она будет уже на компах :) А теперь самое важное – таймслот нашей юзер-группы для встречи с Брайаном Харри – с 16 до 17, место то же что и конференция. Поэтому всем активным членам UG а также тем MVP и opinion leader’ам кто свободно говорит по-английски и хочет пообщаться с Брайаном советую как-то со мной связаться чтобы мы все потом не потерялись.

Также у нас есть несколько промо-кодов на этот эвент, в основном для тех, кто готов пообщаться с Брайаном :)

Visual Studio 2010 After-party

На следующий день после эвента в Питере мы будем “праздновать” выпуск студии, предположительно в Оливе с 19:00. Для тех кто не знает место – Олива это греческий ресторан который достаточно высоко котируется. Средний чек – 1500р на человека.

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

Вот пока и все. Надеюсь скоро начну снова писать осмысленные посты.

Written by Dmitri

6 апреля 2010 at 11:53

Опубликовано в Events