在使用BeginInvoke和EndInvoke时,如何避免传递/存储委托?
编辑 :将实际问题移至顶部。
更新 :发现微软的一个例子,最后隐藏了一些代码。
我的问题是这些:
- 在同一个委托实例上调用多个BeginInvoke调用是否安全,或者我是否必须为每个正在进行的方法调用构建一个新的委托实例?
- 如果我必须为每个实例构造新实例,是否有某种方法可以从IAsyncResult值中获取原始委托?
- 是否有其他更好的方法来为我的类添加异步支持而不是使用委托?
更多信息如下。
我正在为我的一类添加异步支持,并且认为我会这么做。
上这堂课:
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);