James
James

Reputation: 1085

IAsyncResult reference

Why does IAsyncResult require I keep a reference to the Delegate that BeginInvoked it?

I would like to be able to write something like:

new GenericDelegate(DoSomething).BeginInvoke(DoSomethingComplete);

void DoSomethingComplete(IAsyncResult ar)
{
    ar.EndInvoke();
}

Upvotes: 5

Views: 3326

Answers (4)

Greg Beech
Greg Beech

Reputation: 136697

You don't need to keep your own reference to a delegate when doing a normal1 delegate BeginInvoke; you can cast the IAsyncResult to an AsyncResult and retrieve the delegate from the AsyncDelegate property. And before anybody says "that's a dirty hack", it's documented as being valid at MSDN.

The AsyncResult class is used in conjunction with asynchronous method calls made using delegates. The IAsyncResult returned from the delegate's BeginInvoke method can be cast to an AsyncResult. The AsyncResult has the AsyncDelegate property that holds the delegate object on which the asynchronous call was invoked.

So you could write:

new GenericDelegate(DoSomething).BeginInvoke(DoSomethingComplete);

void DoSomethingComplete(IAsyncResult ar)
{
    ((GenericDelegate)((AsyncResult)ar).AsyncDelegate)).EndInvoke();
}

Note that you do still have to know the type of the original delegate (or at least, I haven't found a way around this limitation; then again I haven't tried).


1 By "normal" here I mean a BeginInvoke on a delegate instance, using the compiler-generated method. This technique of casting to AsyncResult is not guaranteed to work when using pre-defined methods, i.e. when using a class which declares its own BeginX/EndX methods. This is because the class may be doing something more clever internally such as blocking on IO completion ports, and may therefore use a different type of IAsyncResult. However, in the scenario as posited, it will work just fine.

Upvotes: 5

Marc Gravell
Marc Gravell

Reputation: 1063338

I find the whole Begin/End pattern unnecessarily complex - so I had a look at wrapping it up (very comparable to what F# uses). Result: no more need to keep the delegate (etc).

Upvotes: 2

chuckj
chuckj

Reputation: 29575

Try passing the delegate as the async state parameter such as,

class Program {
    static void Main(string[] args) {
        Action d = delegate {
            Console.WriteLine("From the delegate");
        };
        var e = new ManualResetEvent(false);
        d.BeginInvoke(r => {
            ((Action)r.AsyncState).EndInvoke(r);
            e.Set();
        }, d);
        e.WaitOne();
    }
}

Upvotes: 0

JaredPar
JaredPar

Reputation: 755131

IAsyncResult is a very ... overused interface. To be a usable interface in so many scenarios it can only really have the properties and fields which are applicable to all scenarios in which it is used. Otherwise APIs would be struggling to provide parameters which were simply not natural to their specific needs.

In only a subset of the circumstances is the creation of a IAsyncResult occur for a delegate. Other scenarios like Control.BeginInvoke do not begin with a delegate and would not be able to supply one to the interface properties if they existed.

IAsyncResult takes the minimalist approach here and has only the properties all uses of it can supply.

Upvotes: 1

Related Questions