简体   繁体   中英

What sort of class does BeginInvoke return? How can I use it to call EndInvoke?

I'm working my way bottom up through ASP.net, and I was setting up an old school delegate pattern:

    public delegate void ProcessRequestDelegate(HttpContext context);

    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        ProcessRequestDelegate asyncDelegate = new ProcessRequestDelegate(ProcessRequest);
        return asyncDelegate.BeginInvoke(context, cb, extraData);
    }

    public void EndProcessRequest(IAsyncResult result)
    {
       //?? profit?
    }

But I realized, I don't know what to cast the IAsyncResult result to in the EndProcessRequest function. I tried:

    public void EndProcessRequest(IAsyncResult result)
    {
        // Compiler error because this is dumb
        ((ProcessRequestDelegate)result).EndInvoke(result);
    }

but obviously you can't cast result to a delegate. So what is the proper way to end the invocation. And more generally, what sort of class does Delegate.BeginInvoke return?

Update

So I tried this out:

    public static void Main()
    {
        IAsyncResult result = BeginStuff("something", null, null);
        IAsyncResult result2 = BeginStuff("something else", null, null);

        EndStuff(result);
        EndStuff(result2);

        Console.ReadLine();
    }

    static Action<String> act = delegate(String str)
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
        Console.WriteLine("This seems to work");
        Thread.Sleep(TimeSpan.FromSeconds(2));
    };

    static Action<String> act2 = delegate(String str) { };

    public static IAsyncResult BeginStuff(String data, AsyncCallback callback, Object state)
    {
        return Program.act.BeginInvoke(data, callback, state);
    }

    public static void EndStuff(IAsyncResult result)
    {
        // This works fine no matter how many times BeginInvoke is called
        Program.act.EndInvoke(result);

        // This however will fail, because the delegate that began the invocation 
        // is not the same as the one that ends it.
        Program.act2.EndInvoke(result);
    }

and it seems to work fine. As long as BeginInvoke / EndInvoke are called using the same delegate, the runtime handles the rest (I guess that's what the IAsyncResult is for). If you try to end using a different delegate though, it will give you a kind reminder not to do that.


Another Update

I found a better-er way to do this:

public void Operation()
{
    throw new FaultException<FrieldyError>(new FrieldyError("returned with this error"));
}

public IAsyncResult BeginOperation(AsyncCallback callback, object state)
{
    return ((Action)Operation).BeginInvoke(callback, state);
}

public void EndOperation(IAsyncResult result)
{
    ((Action)Operation).EndInvoke(result);
}

Since you just need to be using the same type of delegate, you can use one of the pre-existing ones and it works fine. No need to declare a local/private delegate of your own.

Useful!

It shouldnt matter to you what the concrete class returned by Delegate.BeginInvoke is, whatever it is the interface provides all you need in order to wait for its completion (using a WaitHandle or a polling technique [ examples ]) and call EndInvoke .

In addition, you've specified an AsyncCallback parameter, wouldnt you just pass EndProcessRequest to that?

eg,

this.BeginProcessRequest(
        HttpContext.Current, 
        this.EndProcessRequest, 
        "My extra data");

Edit after comments:

If you're implementing IHttpAsyncHandler as your comments describe, you should have even less problems, as you can store the delegate instance as a member of your class

public class ProcessRequestAsyncHandler ; IHttpAsyncHandler 
{
   private  ProcessRequestDelegate asyncDelegate;

   public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        asyncDelegate = new ProcessRequestDelegate(ProcessRequest);
        return asyncDelegate.BeginInvoke(context, cb, extraData);
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        asyncDelegate.EndInvoke(result);
    }

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
       // Do something long-running
    }
}

Reference:

You can cast it as an AsyncResult object. This contains properties such as your delegate and AsyncState (the object you passed in extraData ). To get your delegate, you can try this

((ProcessRequestDelegate)((AsyncResult)result).AsyncDelegate)).EndInvoke();

The AsyncDelegate property gets the delegate which invoked the async call.

Oh yes, it seems you will have to do it that way. Here is what you could do:

public void EndProcessRequest(IAsyncResult result)
{
    ProcessRequestDelegate del = (ProcessrequestDelegate) result.AsyncState;
    object item = del.EndInvoke(result)
}

Loo here at this article.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM