简体   繁体   中英

Shortest way to invoke a method with parameters in C#

When I need to invoke some code in the specified thread, i am using something like this:

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

delegate void MethodToInvokeDelegate(string foo, int bar);

void MethodToInvoke(string foo, int bar)
{
    DoSomeWork(foo);
    DoMoreWork(bar); 
}

void SomeMethod()
{
    string S = "Some text";
    int I = 1;
    dispatcher.BeginInvoke(new MethodToInvokeDelegate(MethodToInvoke), new object[] {S, I});
}

This code works fine, but it's quite heavy. I'd like to make it without declaring MethodToInvoke and MethodToInvokeDelegate - using an anonymous method. But I can't figure out how to pass parameters to it.

I can't write this like:

dispatcher.BeginInvoke((Action)delegate() { DoSomeWork(S); DoMoreWork(I); });

I need to actually pass parameters to method.

Is it any way to write it short and simple?

Example:

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
int[] ArrayToFill = new int[3];

void SomeMethod()
{
    for (int i = 0; i < 3; i++)
        dispatcher.BeginInvoke( { ArrayToFill[i] = 10; } );
}

This code will not work: method will be called with i = 1, 2, 3 and will raise IndexOutOfRange exception. i will be incremented before method begins to execute. So we need to rewrite it like:

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
int[] ArrayToFill = new int[3];

delegate void MethodToInvokeDelegate(int i);

void MethodToInvoke(int i)
{
    ArrayToFill[i] = 10; 
}

void SomeMethod()
{
    for (int i = 0; i < 3; i++)
         dispatcher.BeginInvoke(new MethodToInvokeDelegate(MethodToInvoke), new object[] {i});
}

if you wish to avoid creating a delegate type for each call, make use of Action<> , Action<,> , etc

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
string S = "Some text";
int I = 1;
dispatcher.BeginInvoke(
                        (Action<string, int>)((foo, bar) =>
                        {
                            MessageBox.Show(bar.ToString(), foo);
                            //DoSomeWork(foo);
                            //DoMoreWork(bar); 
                        }), 
                        new object[] { S, I }
                      );

an example with ArrayToFill[j] = 10; action can be fixed very simple:

for (int i = 0; i < 3; i++)
{
    int j = i;
    // lambda captures int variable
    // use a new variable j for each lambda to avoid exceptions
    dispatcher.BeginInvoke(new Action(() =>
    {
        ArrayToFill[j] = 10;
    }));
}

Try this:

string S = "Some text";
int I = 1;

dispatcher.BeginInvoke(new Action(() =>
{
    DoSomeWork(S);
    DoMoreWork(I);
}));

[EDIT]

In response to your modified question:

The issue you are seeing there is modified closure problem .

To fix it, you merely need to copy the argument before invoking the method:

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

int[] ArrayToFill = new int[3];

for (int i = 0; i < 3; i++)
{
    int index = i;
    dispatcher.BeginInvoke(new Action(() => { ArrayToFill[index] = 10; } ));
}
yourUIcontrol.BeginInvoke(new MethodInvoker(delegate {
    //your code goes here
}));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM