Сгенерируйте События в.NET на основном потоке UI

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

Как в стороне, даже если бы я писал этот объект как частный внутренний класс, который я все еще предоставил бы конструктору для упрощения кода для инициализации объекта. Необходимость иметь 3 строки кода для получения применимого объекта, когда каждый сделает, просто грязна.

41
задан Brandon 9 November 2009 в 03:02
поделиться

4 ответа

Ваша библиотека может проверять цель каждого делегата в списке вызовов события и маршалировать вызов целевого потока, если эта цель - ISynchronizeInvoke:

private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
{
  foreach (Delegate d in theEvent.GetInvocationList())
  {
    ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
    if (syncer == null)
    {
      d.DynamicInvoke(args);
    }
    else
    {
      syncer.BeginInvoke(d, args);  // cleanup omitted
    }
  }
}

Другой подход, который делает контракт потоковой передачи более явным является требование, чтобы клиенты вашей библиотеки передавали ISynchronizeInvoke или SynchronizationContext для потока, в котором они хотят, чтобы вы вызывали события. Это дает пользователям вашей библиотеки немного больше видимости и контроля, чем подход «тайной проверки цели делегата».

Что касается вашего второго вопроса, я бы поместил материал для маршаллинга потоков в ваш OnXxx или любой другой API, который вызывает код пользователя. это могло привести к возникновению события.

38
ответ дан 27 November 2019 в 00:22
поделиться

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

Документация по потокам WPF содержит хорошее введение и примеры того, как это сделать.

Вот его суть:

private Dispatcher _uiDispatcher;

// Call from the main thread
public void UseThisThreadForEvents()
{
     _uiDispatcher = Dispatcher.CurrentDispatcher;
}

// Some method of library that may be called on worker thread
public void MyMethod()
{
    if (Dispatcher.CurrentDispatcher != _uiDispatcher)
    {
        _uiDispatcher.Invoke(delegate()
        {
            // UI thread code
        });
    }
    else
    {
         // UI thread code
    }
}
5
ответ дан 27 November 2019 в 00:22
поделиться

Вы можете использовать класс SynchronizationContext для маршалинга вызовов потока пользовательского интерфейса в WinForms или WPF с помощью SynchronizationContext.Current .

12
ответ дан 27 November 2019 в 00:22
поделиться

Вот идея Itwolson, выраженная в качестве метода расширения, который отлично работает для меня:

/// <summary>Extension methods for EventHandler-type delegates.</summary>
public static class EventExtensions
{
    /// <summary>Raises the event (on the UI thread if available).</summary>
    /// <param name="multicastDelegate">The event to raise.</param>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">An EventArgs that contains the event data.</param>
    /// <returns>The return value of the event invocation or null if none.</returns>
    public static object Raise(this MulticastDelegate multicastDelegate, object sender, EventArgs e)
    {
        object retVal = null;

        MulticastDelegate threadSafeMulticastDelegate = multicastDelegate;
        if (threadSafeMulticastDelegate != null)
        {
            foreach (Delegate d in threadSafeMulticastDelegate.GetInvocationList())
            {
                var synchronizeInvoke = d.Target as ISynchronizeInvoke;
                if ((synchronizeInvoke != null) && synchronizeInvoke.InvokeRequired)
                {
                    retVal = synchronizeInvoke.EndInvoke(synchronizeInvoke.BeginInvoke(d, new[] { sender, e }));
                }
                else
                {
                    retVal = d.DynamicInvoke(new[] { sender, e });
                }
            }
        }

        return retVal;
    }
}

Вы тогда просто поднимаете свое мероприятие:

MyEvent.Raise(this, EventArgs.Empty);
26
ответ дан 27 November 2019 в 00:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: