C# 委托向回调函数返回了值

C# 委托向回调函数返回了值,c#,asp.net,multithreading,winforms,C#,Asp.net,Multithreading,Winforms,虽然我认为解决以下问题并不困难,但我遇到了一个非常棘手的问题: 我有一个应用程序,当你点击一个按钮时,它会调用一个委托。看起来是这样的: private void button1_Click(object sender, EventArgs e) { MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber); AsyncCallback completedCallba

虽然我认为解决以下问题并不困难,但我遇到了一个非常棘手的问题: 我有一个应用程序,当你点击一个按钮时,它会调用一个委托。看起来是这样的:

    private void button1_Click(object sender, EventArgs e)
    {
        MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber);
        AsyncCallback completedCallback = new AsyncCallback(DelegateMethod);

        object[] myArray = new object[1];
        myArray[0] = "The number is: ";

        worker.BeginInvoke(completedCallback,myArray);
    }
    private int returnANumber()
    {
        Thread.Sleep(1000);
        return 100;
    }
    private void DelegateMethod(IAsyncResult arr)
    {
        object[] myArray = (object[])arr.AsyncState;

        //Messagebox should show "The number is: 100"
        MessageBox.Show(myArray[0].ToString() + theNumberFromTheMethos);
    }
方法“returnANumber”应该休眠一秒钟,然后返回数字100。 看起来是这样的:

    private void button1_Click(object sender, EventArgs e)
    {
        MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber);
        AsyncCallback completedCallback = new AsyncCallback(DelegateMethod);

        object[] myArray = new object[1];
        myArray[0] = "The number is: ";

        worker.BeginInvoke(completedCallback,myArray);
    }
    private int returnANumber()
    {
        Thread.Sleep(1000);
        return 100;
    }
    private void DelegateMethod(IAsyncResult arr)
    {
        object[] myArray = (object[])arr.AsyncState;

        //Messagebox should show "The number is: 100"
        MessageBox.Show(myArray[0].ToString() + theNumberFromTheMethos);
    }
当睡眠结束并返回号码时,棘手的部分就来了。在click函数中创建的数组被传递给回调方法。这个方法应该显示一个MessageBox,其中显示数组的字符串和从委托返回的数字。函数如下所示:

    private void button1_Click(object sender, EventArgs e)
    {
        MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber);
        AsyncCallback completedCallback = new AsyncCallback(DelegateMethod);

        object[] myArray = new object[1];
        myArray[0] = "The number is: ";

        worker.BeginInvoke(completedCallback,myArray);
    }
    private int returnANumber()
    {
        Thread.Sleep(1000);
        return 100;
    }
    private void DelegateMethod(IAsyncResult arr)
    {
        object[] myArray = (object[])arr.AsyncState;

        //Messagebox should show "The number is: 100"
        MessageBox.Show(myArray[0].ToString() + theNumberFromTheMethos);
    }
我不知道如何访问委托方法返回的值。。。
请帮助我:(

您可以通过
MyTaskWorkerDelegate.EndInvoke
访问返回值。因此,您必须保留对
MyTaskWorkerDelegate
的引用,并将其传递到
DelegateMethod
。按照代码当前的外观,您需要一个单独的对象来通过
myArray
MyTaskWorkerDelegate
传递
IAsyncResult.AsyncState

如果使用匿名lambda/delegate并访问局部变量,则可以避免这种情况并大大简化代码。C#编译器将发挥保持状态的神奇作用:

private void button1_Click(object sender, EventArgs e)
{
    MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber);

    object[] myArray = new object[1];
    myArray[0] = "The number is: ";

    AsyncCallback completedCallback = new AsyncCallback((ar) => 
    {
        var result = worker.EndInvoke(ar);

        // you cannot use MessageBox here, you're on a non-UI random pool thread
        Debug.Print(myArray[0].ToString() + result);
    });

    worker.BeginInvoke(completedCallback, null); // no need to pass the state
}
还有一点,您不能从
AsyncCallback
委托中使用
MessageBox
,调用回调时您处于随机池线程中。您需要使用:

AsyncCallback completedCallback = new AsyncCallback((ar) => 
{
    var result = worker.EndInvoke(ar);

    // you cannot use MessageBox here, you're on a non-UI random pool thread
    this.BeginInvoke(new MethodInvoker(() =>
    {
        // here you can use MessageBox, you're on the UI thread
        MessageBox.Show(myArray[0].ToString() + result);
    }));
});
注意,
Control.BeginInvoke
的用途与
Delegate.BeginInvoke
完全不同,尽管名称相似。它用于在WinForms UI线程上异步调用回调

也就是说,如果您可以使用.NET 4.5(或.NET 4.0+和VS2012+),您可以使用@SLaks指出的和
async/await
来简化代码事件。您的代码可能很简单:

private int returnANumber()
{
    Thread.Sleep(1000);
    return 100;
}

private async void button1_Click(object sender, EventArgs e)
{
    object[] myArray = new object[1];
    myArray[0] = "The number is: ";

    int result = await Task.Run(() => returnANumber());
    MessageBox.Show(myArray[0].ToString() + result);
}
此外,如果
Thread.Sleep(1000)
的唯一原因是暂停,则可以使用
Task.Delay

private async Task<int> returnANumber()
{
    await Task.Delay(1000);
    return 100;
}

private async void button1_Click(object sender, EventArgs e)
{
    object[] myArray = new object[1];
    myArray[0] = "The number is: ";

    int result = await returnANumber();
    MessageBox.Show(myArray[0].ToString() + result);
}
private async Task returnANumber()
{
等待任务。延迟(1000);
返回100;
}
私有异步无效按钮1\u单击(对象发送方,事件参数e)
{
object[]myArray=新对象[1];
myArray[0]=“数字为:”;
int result=wait returnANumber();
显示(myArray[0].ToString()+结果);
}

这样,您就不会显式地使用池线程。

您应该用
Task.Run()
替换所有这些,这更易于使用。我编辑了您的标记并删除了[asp.net]。我假设这是一个WinForms应用程序,就像您使用
MessageBox的方式一样。显示
按钮1\u单击
。非常感谢您的回答,太棒了!我使用TPL解决方案,因为应用程序已经内置在.NET 4.5中。这非常简单!再次感谢!@Coloso,没问题,很高兴它有所帮助。如果您继续使用TPL/a同步/等待,请确保检查此处的链接: