Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否应重新编写所有接口以返回任务<;结果>;?_C#_.net_Async Await - Fatal编程技术网

C# 是否应重新编写所有接口以返回任务<;结果>;?

C# 是否应重新编写所有接口以返回任务<;结果>;?,c#,.net,async-await,C#,.net,Async Await,我有一个简单的界面 public interface SomethingProvider { public Something GetSomething(); } 为了“使”它异步,我会这样做 public interface SomethingProvider { public Task<Something> GetSomethingAsync(); } 但这打破了Liskov替换原则,只会给实现者带来困惑。这取决于您期望接口方法的“速度”。例如,如果实现很简单

我有一个简单的界面

public interface SomethingProvider
{
    public Something GetSomething();
}
为了“使”它异步,我会这样做

public interface SomethingProvider
{
    public Task<Something> GetSomethingAsync();
}
但这打破了Liskov替换原则,只会给实现者带来困惑。

这取决于您期望接口方法的“速度”。例如,如果实现很简单,则如下所示:

public Something GetSomething(){
    var s = new Something();
    if(...)s.SomeField = 1;
    return s;
}
然后返回
任务
方法将产生比性能增益更多的开销。这种方法很可能根本不会对性能产生太大的影响

但是,如果它执行诸如查询数据库、url或非常复杂的计算之类的操作,例如:

public Something GetSomething(){
    Something s = Cache.Get("some key");
    if(s!=null)return s;
    var con = new SqlConnection(...);
    ...;
    return s;
}
然后您需要将其标记为
任务


因此,简言之,这是一个基于具体情况的设计问题,没有绝对的规则可遵循。

当您使用
Task
创建接口时,您告诉接口的实现者:“此方法将异步运行。它将触发将来某个时候完成的工作”。这与方法运行的“速度”无关,异步方法可能会从web请求中快速返回。异步操作上的
wait
确实会触发状态机的开销,但它肯定不能保证执行的“速度”

您不应该根据执行速度将所有方法包装为
任务
,如果它们表示真正的纯异步代码,您应该标记它们

我喜欢来自的
Task
的定义(由于@akshay2000稍加修改)

用计算机科学的术语来说,一项任务是一个未来或一个承诺。(有些人笼统地使用这两个术语,有些人使用不同的术语,没有人能就精确的定义达成一致。)基本上,任务“承诺”会给你一个T,但不是现在,亲爱的,我准备好后再给你回复


此设计问题与
IDisposable
并行。在编写接口时,您必须知道派生类型是否“可能”需要
IDisposable
,如果需要,则从中派生。像测试存根这样的非一次性类型只实现了noop
Dispose

类似地,在编写接口时,您必须知道派生类型是否“可能”使用异步实现

请注意,派生类型的问题是:“实现是否自然异步?”,而不是“实现是否快速?”。速度与此无关。唯一需要考虑的是,实现可能是自然异步的(即使用I/O或其他异步方法)。 当你编写一个接口时,通常有一个(或一小部分)你想要的实现;我建议您在决定使接口方法异步时考虑这些问题。您可以走极端,让每个接口方法都异步,但这就像在任何地方都使用
IDisposable
——这不是我推荐的模式

所以,我想说,如果您(当前)的实现对于某个特定方法都是同步的,那么就让该方法同步;否则,将其设置为异步

正如您所指出的,异步方法签名在技术上意味着实现可能是异步的。测试存根等可以使用
Task.FromResult
同步实现异步签名。就个人而言,我认为这对于存根实现来说是完全可以接受的;但我不建议在当前的所有实现都是同步的情况下“以防万一”使方法异步


另外,我强烈反对两个签名。这需要实现者包装,或者两者都不理想。

你为什么不稍后再来?
我认为这个概念更像是
我准备好后会给你回复。
这不是更合适吗?它的意思是表示
t
不会立即返回,而是在将来某个时候返回。我不会争论确切的定义,因为我没有这样做:)使用返回
任务的方法创建接口不会异步运行任何东西。这里的问题不是“什么是任务”,而是接口在编写时是否应该考虑异步。我没有说让方法返回
Task
可以异步运行任何东西。我说过,只要该方法中没有正在进行的异步过程,就没有理由将其标记为
任务
。你不能强迫每个进程都是异步的,甚至让它看起来像一个。这将迫使实现您的接口的任何人采取变通办法,返回一个
任务
,如果某个任务本质上是同步的,这将是一个坏主意。如果通过“使其异步”,您只需在类似于
Task.Run
的内容中调用已经同步的代码,这也不是一个好主意。虽然这给了您一些“异步”的东西,但您的用户将无法判断它的异步性是否给了他们任何可伸缩性。事实上,这会给它们带来额外的开销,“*Async”并没有暗示这一点。如果您的用户需要异步运行该方法,则应由他们使用
Task.run
See:Related:.我同意。但是,根据接口的性质,无法判断实现是否同步。我倾向于同意,但假设您正在编写
SomethingProvider
接口,并且您不知道人们将如何实现它。将其作为任务返回方法编写不是更灵活吗?因此,如果有人编写
RemoteSomethingProvider
它已经得到支持。反参数-任何人都可以通过使用
新任务(()=>…)将阻塞方法转换为异步方法
新线程(()=>…)。另一方面,任何
任务
都可以转化为阻塞方法
public Something GetSomething(){
    Something s = Cache.Get("some key");
    if(s!=null)return s;
    var con = new SqlConnection(...);
    ...;
    return s;
}