Ross Ross - 25 days ago 8
C# Question

Wrap method & event handler in a single method call

This might be a really stupid question, especially coming from someone who has worked in .Net for a few years. The question is simple:


Is there a way to wrap a method call + the an event handler, created
from that method in a single method call


Based on my understanding, it is not. Here's an example:

// Starts the long running transaction, when this method completes
// we have no idea whether the transaction actually succeeded/failed
// /finished/etc
public bool BeginLongRunningTransaction(/*args*/)
{
myService.LongRunningTransactionComplete += TransactionComplete;

// Runs the actual transaction - this can take some time
myService.ExecuteLongRunningTransaction();
return true;
}

// Handles the long running transaction complete event
// which allows us to see if the transaction suceeded/failed/etc
private void TransactionComplete(/*args*/)
{
/* Stuff complete! */
}


What will happen, is that a caller will call BeginLongRunningTransaction() method, which will begin the long running transaction but will not be able to return a result of that transaction, as that result will come back in TransactionComplete() event handler. What I am trying to see is whether there is a way to both Begin and return the result of the long running transaction inside the BeginLongRunningTransaction() method, so to the caller,

I am aware of sync-await pattern and also know about inline event handlers. In my understanding, neither of those are able to achieve what I am trying to.

The main reason for this question is simplifying the communication from the subscribing client's point of view.

Many thanks!

Answer

Do you mean like this?

public bool DoStuff(/*args*/)
{
    myService.StuffComplete += (/*args*/) => { /* Stuff complete! */ };
    myService.DoStuff();
    return true;
}

But, if you mean that you want the the return value from public bool DoStuff(/*args*/) to be the result of something that happens in /* Stuff complete! */ rather than a hard-coded true then you need to be a bit more clever.

You could do something like this:

public bool DoStuff(/*args*/)
{
    bool result = false;
    myService.StuffComplete += (/*args*/) =>
    {
        result = myService.Status;
    };
    myService.DoStuff();
    return result;
}

Here this code expects that the call runs purely on the current thread. If myService.DoStuff() launches a new thread then the myService.StuffComplete handler will run after public bool DoStuff(/*args*/) has completed and you won't get the result you expect.

If you think that myService does invoke new threads behind the scenes (or you wish to have the code run on a background task) then you should look at using Microsoft's Reactive Framework. Then you can do this:

public IObservable<bool> DoStuff(/*args*/)
{
    return Observable.Create<bool>(o =>
    {
        var subscription =
            Observable
                .FromEventPattern<EventHandler, EventArgs>(
                    h => myService.StuffComplete += h,
                    h => myService.StuffComplete -= h)
                .Select(x => myService.Status)
                .Take(1)
                .Subscribe(o);
        myService.DoStuff();
        return subscription;
    });
}

This changes the output of the method from bool to IObservable<bool>, which means that you can observe the result when it is ready.

DoStuff().Subscribe(result => { /* handle result */ };