2016-01-22 82 views
3

我工作的具有法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 

的第一个获得资源由于不明确的调用而导致第二次编译失败。

嗯,这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找到。

回答

3

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

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

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

那么,我可以给你一个例子,其中类似的问题不是解决。 :)

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

当.NET团队想要添加异步支持时,他们引入了一个新的API:Task.Run,它过载为ActionFunc<Task>。 (请注意,在这种情况下,Task.Run在技术上仅支持StartNew使用的子集,而StartNew未标记为Obsolete)。

他们考虑了他们的选择,但这是他们能想到的最好的选择。请注意,以下问题仍然存在,开发人员在这里提出有关这两个问题的疑问:

  • Task.Run不支持方法组。
  • StartNew将与async lambdas编译得很好,但会表现出惊人的方式(由于async void)。

由于他们改变.NET BCL,向后兼容性是最重要的。对于个人图书馆而言,我更喜欢使用更安全/更清洁的API来实现严格的向后兼容性,所以我会在您的情况下做出其他选择(即,只需添加Func<T, Task>过载作为主要版本升级的一部分)。

+0

感谢Task.Run的好例子。我正在寻找那样的东西。 –

2

有没有一种方法来设计这个公共API,以便用户将只使用无效回调与一个方法和任务返回与另一个。

不,不是真的。问题是Action<T>代表。它已经发现了许多不同的lambda表达式。你将与​​RegisterCallback(Action<T> callback)碰上的一个主要问题 - 如果有人使用这样的:

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

他们正在创造一个async void,这是一个巨大的问题。您对Func<T, Task>有正确的想法。此外,我喜欢你的想法重新命名的功能,并添加“异步”似乎是合适的,RegisterAsyncCallback(Func<string, Task> callback)

+0

这是现货 - 我忘记了'async void'可以投射到'Action '。据我所知,有没有办法编写一个方法,明确禁止异步/非异步委托参数 – Rob

+0

我不知道什么想知道实现?这只是一个回调(价值)。我需要在异步超载中处理更复杂的错误。 –

+0

用更多的代码更新你的小提琴,我会再看一次。我看到的问题是'Action '总是会接受'RegisterCallback (async _ =>等待Task.Delay(0));'。所以没有办法区分。 –

相关问题