我遇到以下问题非常困难,尽管我觉得很难解决: 我有一个应用程序,当您单击按钮时调用代理。它看起来像这样:
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);
}
方法“returnANumber”应该睡一秒钟然后返回数字100。 它看起来像这样:
private int returnANumber()
{
Thread.Sleep(1000);
return 100;
}
当睡眠结束并且返回的数字是棘手的部分。在click函数中创建的数组将传递给回调方法。此方法应显示MessageBox,显示数组的字符串和委托返回的数字。该函数如下所示:
private void DelegateMethod(IAsyncResult arr)
{
object[] myArray = (object[])arr.AsyncState;
//Messagebox should show "The number is: 100"
MessageBox.Show(myArray[0].ToString() + theNumberFromTheMethos);
}
我无法弄清楚如何访问委托方法返回的值... 请帮帮我:(
答案 0 :(得分:1)
您可以通过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
}
另一点是,您无法使用MessageBox
委托中的AsyncCallback
,而是在调用回调时使用随机池线程。您需要使用Control.BeginInvoke
:
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(或。),那么您可以使用TPL和async/await
更多地简化您的代码事件,正如@SLaks所指出的那样。 NET 4.0 + Microsoft.Bcl.Async
和VS2012 +)。您的代码可能就像这样简单:
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);
}
这样,您根本不会显式使用池线程。