包裹方法&单个方法调用中的事件处理程序

时间:2016-11-14 03:35:31

标签: c# .net

这可能是一个非常愚蠢的问题,特别是来自曾在.Net工作过几年的人。问题很简单:

  

有没有办法包装方法调用+创建的事件处理程序   在单个方法调用中使用该方法

根据我的理解,事实并非如此。这是一个例子:

// 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! */
}

会发生什么,调用者将调用BeginLongRunningTransaction()方法,该方法将开始长时间运行的事务,但无法返回该事务的结果,因为结果将会到来回到TransactionComplete()事件处理程序。我想看到的是,是否有一种方法可以同时开始并返回BeginLongRunningTransaction()方法中长时间运行的事务的结果,所以对于调用者

我知道sync-await模式,也知道内联事件处理程序。根据我的理解,这些都不能实现我想要的目标。

这个问题的主要原因是从订阅客户的角度简化了沟通。

非常感谢!

2 个答案:

答案 0 :(得分:1)

你的意思是这样吗?

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

但是,如果您的意思是希望public bool DoStuff(/*args*/)的返回值是/* Stuff complete! */而不是硬编码true的结果,那么您需要更聪明一点。

你可以这样做:

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

此处此代码期望调用纯粹在当前线程上运行。如果myService.DoStuff()启动新主题,那么myService.StuffComplete处理程序将在public bool DoStuff(/*args*/)完成后运行,并且您无法获得预期的结果。

如果您认为myService确实在幕后调用新线程(或者您希望代码在后台任务上运行),那么您应该考虑使用Microsoft的Reactive Framework。然后你可以这样做:

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

这会将方法的输出从bool更改为IObservable<bool>,这意味着您可以在准备就绪时观察结果。

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

答案 1 :(得分:-1)

尝试使用async-await。

小代码示例(我没有测试):

class YourClass
{
    public async Task<bool> DoStuff(/*args*/)
    {
        var handler = new StuffCompleteHandler(myService);
        await handler.GetTask();
        return true;
    }

    private class StuffCompleteHandler
    {
        private ???? myService;
        private TaskCompletionSource<bool> taskSource = new TaskCompletionSource<bool>();

        public StuffCompleteHandler(???? myService)
        {
            this.myService = myService;
            this.myService.StuffComplete += this.StuffComplete;
        }

        private void StuffComplete(/*args*/)
        {
            this.myService.StuffComplete -= this.StuffComplete;
            try
            {
                /* Stuff complete! */
                // do some thing
                this.taskSource.SetResult(true);
            }
            catch (Exception e)
            {
                this.taskSource.SetException(e);
            }
        }

        public Task GetTask() => this.taskSource.Task;
    }
}