区分void和任务返回方法

时间:2016-01-22 12:48:38

标签: c# async-await

我正在开发一个具有Action<T>参数的方法的类:

public void RegisterCallback<T> (Action<T> callback);

这很好用。

instance.RegisterCallback<string>(Callback); //method group
instance.RegisterCallback<string>(t => {}); //lambda 

现在我想为这个接受async方法的方法创建一个重载。这样我就可以将它用于任务返回回调并以不同的方式对待它们。

instance.RegisterCallback<string>(AsyncCallback); //method group with async

AsyncCallback

的位置
    private Task AsyncCallback(string s)
    {
        return Task.Delay(0);
    }

天真的方法是采用这样的方法:

void RegisterCallback<T>(Func<T, Type> callback);

但它存在一些问题:

//1
instance.RegisterCallback<string>(async t => await Task.Delay(0));
//2
instance.RegisterCallback<string>(AsyncCallback); //method group with async

第一个被解析为Action<T> callback重载,第二个由于模糊调用而无法编译。

那么,makes sense我对这个界面很满意:

 void RegisterAsyncCallback<T>(Func<T, Task> callback);   

但是这两个调用编译没有问题:

   instance.RegisterCallback<string>(async t => await Task.Delay(0));
   instance.RegisterAsyncCallback<string>(async t => await Task.Delay(0));

有没有办法设计这个公共api,以便用户只使用void回调一个方法,而任务返回另一个方法。

也许有人可以指出我现有的api,类似的问题已经解决了?

可以找到完整的代码here

2 个答案:

答案 0 :(得分:3)

这里没有好的解决方案;你可能会遇到方法重载的模糊调用错误,或者你会有一个全新的API。

就个人而言,我会添加Func<T, Task>重载并进行重大更改,不再允许方法重载。或者,您可以在新方法(RegisterCallbackEx?)上创建两个重载并将旧方法标记为Obsolete - 这将允许旧代码编译,但鼓励开发人员更改调用。

  

也许有人可以指出我现有的api,类似的问题已经解决了?

好吧,我可以举个例子解决类似问题 的问题。 :)

Task.Factory.StartNew通常用于将工作排队到线程池。这是基于Action的,我相信它适用于方法组。

当.NET团队想要添加异步支持时,他们引入了一个新的API:Task.Run,它为ActionFunc<Task>重载。

(请注意,在这种情况下,Task.Run在技术上仅支持StartNew次使用的子集,而StartNew未标记为Obsolete

他们考虑了他们的选择,但这是他们想出的最好的选择。请注意,以下问题仍然存在,开发人员在SO上定期提出有关这两点的问题:

  • Task.Run不支持方法组。
  • StartNew将使用async lambdas编译得很好,但会以令人惊讶的方式运行(由于async void)。

由于他们正在改变.NET BCL,因此向后兼容性至关重要。对于个人库,我更喜欢更安全/更干净的API而不是严格的向后兼容性,因此我会在您的情况下做出另一种选择(即,只需添加Func<T, Task>重载作为主要版本升级的一部分。)

答案 1 :(得分:2)

  

有没有办法设计这个公共api,以便用户只使用一个方法的void回调和另一个方法返回的任务。

不,不是真的。问题是Action<T>代表。它允许您已经发现的大量各种lambda表达式。您将遇到RegisterCallback(Action<T> callback)的一个主要问题 - 如果有人像这样使用它:

instance.RegisterCallback<string>(async _ => await Task.Delay(0));

他们正在创建async void,这是一个很大的问题。你对Func<T, Task>有了正确的想法。另外,我喜欢你的想法,重命名该功能并添加&#34; Async&#34;似乎合适,RegisterAsyncCallback(Func<string, Task> callback)