Обработка событий с анонимным делегатом

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

Мой конкретный сценарий таков:

В Silverlight 4 метод myFrameworkElement.FindName("otherElementName") теперь работает нормально, но я столкнулся с проблемой. Он все равно возвращает null, когда элемент еще не добавлен в визуальное дерево.

Но теперь мне нужна эта функциональность в DependencyProperty PropertyChangedCallback пользовательского обработчика UserControl. В этой области пока неизвестно добавление UserControl к визуальному дереву. Но я должен выполнить определенное действие над другим элементом в дереве. Когда элемент уже доступен, его можно и нужно делать прямо сейчас. Если нет, это должно быть сделано немедленно, когда оно доступно. Поэтому я придумал этот метод расширения, который я могу назвать следующим образом:

myFrameworkElement.FindNameEnsured("otherElementName",
    result => this.DoSomethingWith(result));

Код для метода расширения выглядит следующим образом:

    static public void FindNameEnsured(this FrameworkElement self,
            string name, Action<object> resultAction)
    {
        if (self != null && resultAction != null)
        {
            object result = self.FindName(name);

            if (result != null)
            {
                resultAction(result);
            }
            else
            {
                RoutedEventHandler handler = null;
                handler = (sender, e) =>
                     {
                         result = self.FindName(name);
                         resultAction(result);

                         self.Loaded -= handler;
                     };

                self.Loaded += handler;
            }
        }

Как вы можете видеть, я должен использовать анонимный делегат, потому что мне нужны значения для name и resultAction внутри обработчика. Затем я отключаю событие внутри обработчика, потому что я умный и чистый парень и не хочу утечки. Я также не хочу ломать любые мухи на колесах здесь с некоторыми фантастическими WeakEventFactories или подобными вещами.

Теперь это работает гладко до сих пор. Но у меня есть некоторые вопросы.

  • Является ли это вообще достаточно понятным подходом к отмене обработчика событий внутри обработчика? Или это собирается убить невинного щенка в конце концов?
  • Могут ли быть некоторые проблемы, такие как утечка из-за использования внешних переменных области внутри анонимного делегата?
  • Могут ли быть проблемы с синхронизацией потоков, которые заставили бы меня "пропустить" событие Loaded? В этом специальном сценарии должен быть задействован только поток диспетчера Silverlight UI. Но если это так или иначе, и/или если мне нужна аналогичная функциональность в сценарии, не относящемся к пользовательскому интерфейсу, какой лучший подход к f1x0r?

Спасибо за ваше терпение и время, читая мои длительные разработки.; -)

Ответ 1

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