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

时间:2009-09-27 20:11:32

标签: c# asynchronous delegates

修改:将实际问题移至顶部。

更新:找到了微软的一个例子,最后收集了一些代码。

我的问题是这些:

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

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

    上课:

    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);
        }
    }
    

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


    更新:我在Asynchronous Delegates Programming Sample上发现了Microsoft的此示例。它显示将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);
        }
    }
    

2 个答案:

答案 0 :(得分:4)

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

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

您可以随时将其捕获到代理中;类似于此处的内容:Async without the Pain,可让您使用Action回调(或Action<T>)。

其他常见模式涉及回调事件,也许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<int>(
            (ac,o)=>calc.BeginAdd(x,y,ac,o),
            calc.EndAdd, result =>
            {
                Console.WriteLine(result());
            });
        Console.ReadLine();
    }
}
static class Async
{
    public static void Run<T>(
    Func<AsyncCallback, object, IAsyncResult> begin,
    Func<IAsyncResult, T> end,
    Action<Func<T>> callback)
    {
        begin(ar =>
        {
            T result;
            try
            {
                result = end(ar); // ensure end called
                callback(() => result);
            }
            catch (Exception ex)
            {
                callback(() => { throw ex; });
            }
        }, null);
    }
}

答案 1 :(得分:2)

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

获得IAsyncResult之后:

IAsyncResult ar;    // this has your IAsyncResult from BeginInvoke in it

MethodInvoker d = (MethodInvoker)ar.AsyncState;
d.EndInvoke(ar);