Автоматизация выкройка свойство invokerequired код


я стал болезненно осознавать, как часто нужно писать следующий шаблон кода в событийном коде GUI, где

private void DoGUISwitch() {
    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;
}

будет:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

это неудобный шаблон в C#, как для запоминания, так и для ввода. Кто-нибудь придумал какой-то ярлык или конструкцию, которая автоматизирует это до некоторой степени? Было бы здорово, если бы был способ прикрепить функцию к объектам, которые делают эту проверку без необходимости проходить всю эту дополнительную работу, например а object1.InvokeIfNecessary.visible = true ярлык тип.

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

Итак, кто-нибудь выяснил какие-либо ярлыки?

9 165

9 ответов:

подход ли может быть упрощен еще больше

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    // See Update 2 for edits Mike de Klerk suggests to insert here.

    if (control.InvokeRequired) {
        control.Invoke(action);
    } else {
        action();
    }
}

и можно назвать такой

richEditControl1.InvokeIfRequired(() =>
{
    // Do anything you want with the control here
    richEditControl1.RtfText = value;
    RtfHelpers.AddMissingStyles(richEditControl1);
});

нет необходимости передавать элемент управления в качестве параметра делегату. C# автоматически создает закрытие.


обновление:

Согласно нескольким другим плакатам Control можно обобщить как ISynchronizeInvoke:

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
                                         MethodInvoker action)
{
    if (obj.InvokeRequired) {
        var args = new object[0];
        obj.Invoke(action, args);
    } else {
        action();
    }
}

Донбойтнотт указал, что в отличие от Control the ISynchronizeInvoke интерфейс требует массив объектов для Invoke метод как список параметров для action.


обновление 2

изменения, предложенные Майком де Клерком (см. комментарий в 1-м фрагменте кода для точки вставки):

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
    System.Threading.Thread.Sleep(50);
}

см. комментарий ToolmakerSteve ниже для беспокойства по поводу этого предложения.

вы можете написать метод расширения:

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

и использовать его так:

object1.InvokeIfRequired(c => { c.Visible = true; });

EDIT: как указывает Симпзон в комментариях, вы также можете изменить подпись на:

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control

вот форма, которую я использовал во всем моем коде.

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    });
} 

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

надеюсь, что это помогает.

создать ThreadSafeInvoke.фрагмент файла, а затем вы можете просто выбрать инструкции обновления, щелкните правой кнопкой мыши и выберите " окружить С...'или Ctrl-K+S:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>ThreadsafeInvoke</Title>
    <Shortcut></Shortcut>
    <Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="CSharp">
      <![CDATA[
      Invoke( (MethodInvoker) delegate
      {
          $selected$
      });      
      ]]>
    </Code>
  </Snippet>
</CodeSnippet>

вот улучшенная / комбинированная версия ответов ли, Оливера и Стефана.

public delegate void InvokeIfRequiredDelegate<T>(T obj)
    where T : ISynchronizeInvoke;

public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
    where T : ISynchronizeInvoke
{
    if (obj.InvokeRequired)
    {
        obj.Invoke(action, new object[] { obj });
    }
    else
    {
        action(obj);
    }
} 

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

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});

Я бы предпочел использовать один экземпляр делегата метода вместо того, чтобы каждый раз создавать новый экземпляр. В моем случае я использовал, чтобы показать прогресс и (информация/ошибка) сообщения от Backroundworker копирования и приведения больших данных из экземпляра sql. Каждый раз после примерно 70000 вызовов прогресса и сообщений моя форма перестала работать и показывать новые сообщения. Это не произошло, когда я начал использовать один глобальный делегат экземпляра.

delegate void ShowMessageCallback(string message);

private void Form1_Load(object sender, EventArgs e)
{
    ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}

private void ShowMessage(string message)
{
    if (this.InvokeRequired)
        this.Invoke(showMessageDelegate, message);
    else
        labelMessage.Text = message;           
}

void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
    ShowMessage(e.Message);
}

использование:

control.InvokeIfRequired(c => c.Visible = false);

return control.InvokeIfRequired(c => {
    c.Visible = value

    return c.Visible;
});

код:

using System;
using System.ComponentModel;

namespace Extensions
{
    public static class SynchronizeInvokeExtensions
    {
        public static void InvokeIfRequired<T>(this T obj, Action<T> action)
            where T : ISynchronizeInvoke
        {
            if (obj.InvokeRequired)
            {
                obj.Invoke(action, new object[] { obj });
            }
            else
            {
                action(obj);
            }
        }

        public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func) 
            where TIn : ISynchronizeInvoke
        {
            return obj.InvokeRequired
                ? (TOut)obj.Invoke(func, new object[] { obj })
                : func(obj);
        }
    }
}

Мне нравится делать это немного по-другому, мне нравится называть "себя", если это необходимо с действием,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

это удобный шаблон, IsFormClosing-это поле, которое я устанавливаю в True, когда я закрываю свою форму, поскольку могут быть некоторые фоновые потоки, которые все еще работают...

вы никогда не должны писать код, который выглядит так:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

Если у вас есть код, который выглядит так, то ваше приложение не является потокобезопасным. Это означает, что у вас есть код, который уже вызывает DoGUISwitch() из другого потока. Слишком поздно проверять, находится ли он в другом потоке. InvokeRequire должен быть вызван, прежде чем сделать вызов DoGUISwitch. Вы не должны получать доступ к любому методу или свойству из другого нитка.

ссылки: управление.InvokeRequired Свойство где вы можете прочитать следующее:

в дополнение к свойству InvokeRequired существует четыре метода элемент управления, потокобезопасный для вызова: Invoke, BeginInvoke, EndInvoke и CreateGraphics, если дескриптор для элемента управления уже был создан.

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