简体   繁体   English

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

[英]How can I avoid having to pass around/store a delegate when using BeginInvoke and EndInvoke?

Edit : Moved the actual question to the top. 编辑 :将实际问题移至顶部。

Update : Found an example by Microsoft, tucked on some more code at the end. 更新 :发现微软的一个例子,最后隐藏了一些代码。

My questions are these: 我的问题是这些:

  1. Is it safe to call multiple BeginInvoke calls on the same delegate instance, or do I have to construct a new delegate instance for each in-flight method call? 在同一个委托实例上调用多个BeginInvoke调用是否安全,或者我是否必须为每个正在进行的方法调用构建一个新的委托实例?
  2. If I have to construct new instances for each, is there some way to get hold of the original delegate out of the IAsyncResult value? 如果我必须为每个实例构造新实例,是否有某种方法可以从IAsyncResult值中获取原始委托?
  3. Is there some other, better, way to add asynchronous support to my class than using delegates? 是否有其他更好的方法来为我的类添加异步支持而不是使用委托?

More info follows. 更多信息如下。

I am adding asynchronous support to a class of mine, and thought I'd do it simple. 我正在为我的一类添加异步支持,并认为我会这么做。

Take this class: 上这堂课:

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

I thought I could simply do this: 我以为我可以这样做:

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

This doesn't work, as the two methods each construct their own delegate object, and the .EndInvoke call checks to see if the delegate instance i call it on is the same as the one I originally called BeginInvoke on. 这不起作用,因为两个方法各自构造自己的委托对象,并且.EndInvoke调用检查以查看我调用它的委托实例是否与我最初调用BeginInvoke的实例相同。

The simplest way to handle this would be to just store a reference into a variable, like this: 处理这个问题的最简单方法是将引用存储到变量中,如下所示:

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

Note that I'm fully aware of problems with allowing multiple instance methods on the same class to execute at the same time, with regards to shared state, etc. 请注意,我完全了解允许同一个类上的多个实例方法同时执行的问题,关于共享状态等。


Update : I found this example here by Microsoft on Asynchronous Delegates Programming Sample . 更新 :我在这里通过Microsoft在异步委托编程示例中找到了这个示例。 It shows casting the IAsyncResult reference back to an AsyncResult object, and then I can get the original delegate instance through the AsyncDelegate property. 它显示了将IAsyncResult引用转换回AsyncResult对象,然后我可以通过AsyncDelegate属性获取原始委托实例。

Is this a safe approach? 这是一种安全的方法吗?

In other words, is the following class fine? 换句话说,以下课程是否合适?

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

Edit: if you just mean the delegate itself - I think you can just do: 编辑:如果你只是指代表本身 - 我你可以这样做:

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

You could always capture it into the delegate; 你总是可以把它捕获到代表中; something like here: Async without the Pain , which lets you just use an Action callback (or Action<T> ). 像这里的东西: Async without the Pain ,它允许你只使用Action回调(或Action<T> )。

Other common patterns involve events for the callback, and perhaps ThreadPool.QueueUserWorkItem ; 其他常见模式涉及回调事件,也许还有ThreadPool.QueueUserWorkItem ; a lot simpler than IAsyncResult . IAsyncResult简单得多。


Putting all this together; 将所有这些放在一起; here's an example where neither the caller nor the code needs to get stressed about 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);
    }
}

I always store the delegate instance I called it on as part of the AsyncState of a BeginInvoke, that way you can always get it without having to rely on a specific implementation of IAsyncResult 我总是存储我作为BeginInvoke的AsyncState的一部分调用它的委托实例,这样你总是可以得到它而不必依赖于IAsyncResult的特定实现

Once you've got your IAsyncResult: 一旦你得到了你的IAsyncResult:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用任务来简化Delegate.BeginInvoke / Delegate.EndInvoke? - How to use Tasks to simplify Delegate.BeginInvoke/Delegate.EndInvoke? Delegate.BeginInvoke()/ EndInvoke()实现 - Delegate.BeginInvoke()/EndInvoke() implementation 为什么BackgroundWorker在代理上调用BeginInvoke时不需要调用EndInvoke? - Why does BackgroundWorker not need to call EndInvoke when it calls BeginInvoke on a delegate? 为什么在Delegate类中看不到BeginInvoke和EndInvoke方法的存在? - Why do I not see the presence of BeginInvoke and EndInvoke methods in Delegate class? 有效地使用begininvoke / endinvoke - Using begininvoke/endinvoke efficiently BeginInvoke返回哪种类型的类? 如何使用它调用EndInvoke? - What sort of class does BeginInvoke return? How can I use it to call EndInvoke? 谁将BeginInvoke,Invoke和EndInvoke方法定义添加到类型委托? - Who adds BeginInvoke, Invoke and EndInvoke method definitions to a typed delegate? 为什么我不能直接在方法上调用BeginInvoke,而不必将其分配给委托? - Why can't I call BeginInvoke directly on a method rather than having to assign it to a delegate? 使用Dispatcher.BeginInvoke()时如何避免出现竞争情况? - How do I avoid a race condition when using Dispatcher.BeginInvoke()? 以多线程方式使用BeginInvoke / EndInvoke。 AsyncCallback,AsyncWaitHandle和IsCompleted如何交互? - Using BeginInvoke/EndInvoke in a multithreaded fashion. How do AsyncCallback, AsyncWaitHandle and IsCompleted interact?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM