从动作获取回调值

时间:2019-04-04 09:49:26

标签: c#

我具有带操作参数的此函数,执行该参数后,该函数上的参数将具有值。我有办法获得价值吗?

public void DetailsAsync(string param1, string param2,Action<IList<Detail>> callback)
{
       //process happen here and will have a callback to produce the data for detail
}

public class DetailController:ApiController
{
  private IList<Detail> details;

  private DetailCompleted(IList<Detail> detail)
  {
    //now detail parameter has a value that I can use
    details = detail;
  }


  [HttpGet]
  public IList<Detail> GetDetails()
  {
    ServiceManager.DetailsAsync("param1","param2",detailsCompleted)

   //after ServiceManager.DetailsAsync it will go to return details
    return details;
  }
}

尝试此代码时,我在返回详细信息上放置了一个断点,在detailsCompleted上放置了断点,但是发生的是,当我调用Web api GetDatails时,它将首先执行返回详细信息,然后立即执行detailsCompleted函数。这就是为什么我目前无法获得价值的原因。

3 个答案:

答案 0 :(得分:2)

Action<T>的返回值默认为void。如果要返回值,则应使用Func<T,TResult>

https://docs.microsoft.com/en-us/dotnet/api/system.func-2?view=netframework-4.7.2

答案 1 :(得分:2)

我认为这里的问题是,DetailsAsync()(按名称暗示)是异步的,您在等待DetailsAsync()的结果之前返回详细信息。 因此,您应该await,但由于DetailsAsync返回void,所以您不能这样做。

因此,您可以将DetailsAsync封装在一个Task中,然后.Wait()包裹起来,但这有点烂,因为您将阻塞调用线程。

[HttpGet]
public IList<Detail> GetDetails()
{
    Task.Run(() => 
        ServiceManager.DetailsAsync("param1", "param2", detailsCompleted)
    ).Wait();
    return details;
}

答案 2 :(得分:1)

由于DetailsAsync的编写方式,您将需要某种信令系统来暂停GetDetails的执行,直到触发回调为止。 options有几个,但是我选择一个AutoResetEvent是因为它很容易使用和理解。

(我已经更改了一些返回类型,以便不必创建伪造的类来匹配您的代码)

public class DetailController
{
    private IList<int> details;
    private AutoResetEvent callbackSignal = new AutoResetEvent(false);

    private void DetailCompleted(IList<int> detail)
    {
        details = detail;
        callbackSignal.Set();
    }    

    public IList<int> GetDetails()
    {    
        ServiceManager.DetailsAsync("param1", "param2", DetailCompleted);
        callbackSignal.WaitOne();
        return details;
    }
}

callbackSignal.WaitOne();将一直阻塞,直到“已发送信号”为止。在回调方法中,callbackSignal.Set();发送信号,告诉等待事件的任何人现在可以继续进行。

在不确切知道如何实现DetailsAsync的情况下,我不能保证这会成功,但是我充满希望。如果需要,您可能还必须添加一些其他保护以确保它是完全可重入的。


如果您想使用更现代的async / await模式,则可以使用DetailsAsync返回并使用的方法包装对Task访问的访问权,并使用TaskCompletionSource来编排回调并返回值。

public class DetailController
{
    public async Task<IList<int>> GetDetails()
    {
        var details = await ServiceWrapper.GetDetails();
        return details;
    }
}

public static class ServiceWrapper
{
    public static Task<IList<int>> GetDetails()
    {
        var tcs = new TaskCompletionSource<IList<int>>();
        ServiceManager.DetailsAsync("param1", "param2", (IList<int> details) =>
            {
                tcs.SetResult(details);
            });
        return tcs.Task;
    }
}