在使用BeginInvoke和EndInvoke时,如何避免传递/存储委托?

编辑 :将实际问题移至顶部。

更新 :发现微软的一个例子,最后隐藏了一些代码。

我的问题是这些:

  1. 在同一个委托实例上调用多个BeginInvoke调用是否安全,或者我是否必须为每个正在进行的方法调用构建一个新的委托实例?
  2. 如果我必须为每个实例构造新实例,是否有某种方法可以从IAsyncResult值中获取原始委托?
  3. 是否有其他更好的方法来为我的类添加异步支持而不是使用委托?

更多信息如下。

我正在为我的一类添加异步支持,并且认为我会这么做。

上这堂课:

public class Calculator { public Int32 Add(Int32 a, Int32 b) { return a + b; } } 

我以为我可以这样做:

 public class Calculator { private delegate Int32 AddDelegate(Int32 a, Int32 b); public Int32 Add(Int32 a, Int32 b) { return a + b; } public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj) { return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); } public Int32 EndAdd(IAsyncResult ar) { return new AddDelegate(Add).EndInvoke(ar); } } 

这不起作用,因为这两个方法都构造了自己的委托对象,而.EndInvoke调用检查我调用它的委托实例是否与我最初调用BeginInvoke的实例相同。

处理这个问题的最简单方法是将引用存储到变量中,如下所示:

 public class Calculator { private delegate Int32 AddDelegate(Int32 a, Int32 b); private AddDelegate _Add; public Calculator() { _Add = new AddDelegate(Add); } public Int32 Add(Int32 a, Int32 b) { return a + b; } public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj) { return _Add.BeginInvoke(a, b, callback, obj); } public Int32 EndAdd(IAsyncResult ar) { return _Add.EndInvoke(ar); } } 

请注意,我完全了解允许同一个类上的多个实例方法同时执行的问题,关于共享状态等。


更新 :我在这里通过Microsoft在Asynchronous Delegates Programming Sample上找到了这个例子。 它显示了将IAsyncResult引用转换回AsyncResult对象,然后我可以通过AsyncDelegate属性获取原始委托实例。

这是一种安全的方法吗?

换句话说,以下课程是否合适?

 public class Calculator { private delegate Int32 AddDelegate(Int32 a, Int32 b); public Int32 Add(Int32 a, Int32 b) { return a + b; } public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj) { return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); } public Int32 EndAdd(IAsyncResult ar) { AddDelegate del = (AddDelegate)((AsyncResult)ar).AsyncDelegate; return del.EndInvoke(ar); } } 

编辑:如果你只是指代表本身 – 我你可以这样做:

 public Int32 EndAdd(IAsyncResult ar) { var d = (AddDelegate)((AsyncResult)ar).AsyncDelegate; return d.EndInvoke(ar); } 

你总是可以把它捕获到代表中; 类似这里的东西: Async without the Pain ,它允许你只使用Action回调(或Action )。

其他常见模式涉及回调事件,也许还有ThreadPool.QueueUserWorkItem ; 比IAsyncResult简单得多。


将所有这些放在一起; 这里是一个示例,其中调用者和代码都不需要对IAsyncResult感到压力:

 using System; using System.Runtime.Remoting.Messaging; public class Calculator { private delegate Int32 AddDelegate(Int32 a, Int32 b); public Int32 Add(Int32 a, Int32 b) { return a + b; } public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj) { return new AddDelegate(Add).BeginInvoke(a, b, callback, obj); } public Int32 EndAdd(IAsyncResult ar) { var d = (AddDelegate)((AsyncResult)ar).AsyncDelegate; return d.EndInvoke(ar); } } static class Program { static void Main() { Calculator calc = new Calculator(); int x = 1, y = 2; Async.Run( (ac,o)=>calc.BeginAdd(x,y,ac,o), calc.EndAdd, result => { Console.WriteLine(result()); }); Console.ReadLine(); } } static class Async { public static void Run( Func begin, Func end, Action> callback) { begin(ar => { T result; try { result = end(ar); // ensure end called callback(() => result); } catch (Exception ex) { callback(() => { throw ex; }); } }, null); } } 

我总是存储我作为BeginInvoke的AsyncState的一部分调用它的委托实例,这样你总是可以得到它而不必依赖于IAsyncResult的特定实现

一旦你获得了IAsyncResult:

 IAsyncResult ar; // this has your IAsyncResult from BeginInvoke in it MethodInvoker d = (MethodInvoker)ar.AsyncState; d.EndInvoke(ar);