Automatyzacja wywoływanego wzorca kodu

Boleśnie zdałem sobie sprawę z tego, jak często trzeba pisać następujący wzór kodu w sterowanym zdarzeniami kodzie GUI, gdzie

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

Staje się:

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

Jest to niezręczny Wzór W C#, zarówno do zapamiętania, jak i do wpisania. Czy ktoś wymyślił jakiś skrót lub konstrukcję, która automatyzuje to do pewnego stopnia? Byłoby fajnie, gdyby istniał sposób na dołączenie funkcji do obiektów, która robi to sprawdzanie bez konieczności przechodzenia przez całą tę dodatkową pracę, jak typ object1.InvokeIfNecessary.visible = true Skrót.

Poprzednie odpowiedzi omawiały niepraktyczność wywołania Invoke() za każdym razem, a nawet wtedy składnia Invoke() jest zarówno nieefektywna, jak i nadal niezręczna w radzeniu sobie.

Czy ktoś zna jakieś skróty?
Author: Community, 2010-03-03

9 answers

Podejście Lee można jeszcze uprościć

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();
    }
}

I można tak nazwać

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

Nie ma potrzeby przekazywania kontrolki jako parametru delegatowi. C # automatycznie tworzy zamknięcie .


UPDATE :

Według kilku innych plakatów {[4] } można uogólnić jako ISynchronizeInvoke:

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

DonBoitnott zwrócił uwagę, że w przeciwieństwie do Control Interfejs ISynchronizeInvoke wymaga tablicy obiektów dla metody Invoke jako parametru lista action.


UPDATE 2

Zmiany sugerowane przez Mike ' a de Klerka (zobacz komentarz w 1. fragmencie kodu dla Wstawienia punktu):

// 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);
}

Zobacz komentarz ToolmakerSteve poniżej dla obaw dotyczących tej sugestii.

 119
Author: Olivier Jacot-Descombes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-02 13:04:48

Możesz napisać metodę rozszerzenia:

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

I użyj go w ten sposób:

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

EDIT: jak zaznacza Simpzon w komentarzach można też zmienić podpis na:

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control
 128
Author: Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-18 18:55:11

Oto formularz, którego używam w całym kodzie.

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

Oparłem to na wpisie na blogu tutaj . Nie miałem takiego podejścia, więc nie widzę powodu, aby komplikować mój kod z sprawdzeniem właściwości InvokeRequired.

Mam nadzieję, że to pomoże.
 33
Author: Matt Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-13 08:05:12

Utwórz ThreadSafeInvoke.urywek pliku, a następnie możesz po prostu wybrać Instrukcje aktualizacji, kliknąć prawym przyciskiem myszy i wybrać " Surround With...'lub 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>
 9
Author: Aaron Gage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-15 13:42:56

Oto ulepszona / połączona Wersja odpowiedzi Lee, Olivera i Stephana.

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);
    }
} 

Szablon pozwala na elastyczny i mniej rzucany kod, który jest znacznie bardziej czytelny, podczas gdy dedykowany delegat zapewnia wydajność.

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});
 6
Author: gxtaillon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-07 17:50:04

Wolałbym używać pojedynczej instancji delegata metody zamiast tworzyć nową instancję za każdym razem. W moim przypadku używałem do wyświetlania postępu i (info/error) wiadomości z Backroundworker kopiowania i odlewania dużych danych z instancji sql. Co jakiś czas po około 70000 postępach i komunikatach mój formularz przestał działać i pokazywał nowe wiadomości. Nie miało to miejsca, gdy zacząłem używać pojedynczego delegata instancji globalnej.

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);
}
 3
Author: stephan Schmuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-21 11:29:00

Użycie:

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

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

    return c.Visible;
});

Kod:

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);
        }
    }
}
 3
Author: Konstantin S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 11:17:13

Lubię robić to trochę inaczej, lubię nazywać się" sobą " w razie potrzeby za pomocą akcji,

    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

Jest to poręczny wzorzec, pole IsFormClosing jest polem, które ustawiam na True, gdy zamykam formularz, ponieważ mogą istnieć wątki tła, które nadal działają...

 0
Author: Walter Verhoeven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 22:07:11

Nigdy nie powinieneś pisać kodu, który wygląda tak:

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

Jeśli masz kod, który wygląda tak, to Twoja aplikacja nie jest bezpieczna dla wątków. Oznacza to, że masz kod, który już wywołuje DoGUISwitch () z innego wątku. Za późno, żeby sprawdzić, czy jest w innym wątku. InvokeRequire musi zostać wywołany, zanim zadzwonisz do DoGUISwitch. Nie należy uzyskiwać dostępu do żadnej metody lub właściwości z innego wątku.

Odniesienie: Kontrola.InvokeRequired Property gdzie można przeczytać:

Oprócz właściwości InvokeRequired, istnieją cztery metody na kontrolka, która jest Bezpieczna do wywołania: Invoke, BeginInvoke, EndInvoke i CreateGraphics jeśli uchwyt do sterowania został już stworzony.

W jednej architekturze procesora nie ma problemu, ale w architekturze wielu procesorów można spowodować przypisanie części wątku interfejsu do procesora, gdzie kod był uruchomiony...a jeśli ten procesor jest inny od tego, gdzie działał wątek UI, to po zakończeniu wątku wywołującego Windows pomyśli, że wątek UI się zakończył i zabije proces aplikacji, tzn. Twoja aplikacja zakończy się bez błędu.

 -3
Author: Steve Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 13:10:30