在您的DoSomething
示例中,编译器没有抱怨,因为您的 MyAwaitableGetResult
方法的类型与THuh
. 相关的语句THuh
是return null;
。null 文字可以隐式转换为THuh
,所以一切都很好。
IEnumerable
类似于的关键字await
是foreach
。 await
需要适合特定模式的类型,foreach
. 一种是消费可等待类型的机制,另一种是消费可枚举类型的机制。
另一方面,迭代器块 (yield return
和yield break
) 是用于定义可枚举类型的机制(通过编写方法而不是显式声明类型)。这里的比喻是async
关键字。
async
为了详细说明and之间的类比yield return
,请注意返回的迭代器块IEnumerable<int>
可以包含yield return 42;
类似的语句,返回的异步方法Task<int>
可以包含语句yield return 42;
。请注意,在这两种情况下,返回表达式的类型不是方法的返回类型,而是方法返回类型的类型参数。
如果你还没有这样做,你真的应该阅读 Eric Lippert 关于这些主题的博客:
http://blogs.msdn.com/b/ericlippert/archive/tags/Async/
http://blogs.msdn.com/b/ericlippert/archive/tags/Iterators/
此外,如果这个概念对您来说是新的(就像对我一样),关于 Async 系列以外的关于 continuation-passing 风格的帖子可能会很有用:
http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/
最后,有关示例,请参阅Eric 的博客文章链接到他的 MSDN 文章和同一问题中的相关文章以及 Bill Wagner 在http://msdn.microsoft.com/en-us/vstudio/hh533273上的后续文章
编辑
我看到一些似乎不遵循此规则的示例 - 返回值似乎可以达到,并且该值是非默认值。这个值是不可访问的吗?如果是这样,为什么尴尬的无法访问的 return 语句?
短语“body 的端点必须不可到达”意味着你必须有一个 return 语句。主体的端点位于 return 语句之后,并且通过 return 语句变得不可访问。使用正常的 int 返回方法的示例:
public int Main()
{
Console.WriteLine("X");
//after this comment is the reachable end point of the body; this method therefore won't compile.
}
public int Main()
{
Console.WriteLine("X");
return 0;
//anything after the return statement is unreachable, including the end point of the body; this method therefore will compile.
}
编辑 2
这是一个计算字符串后半部分的等待器的简短示例。该示例传递了一个将结果打印到控制台的延续。这不是线程安全的!
public static class StringExtensions
{
public static SubstringAwaiter GetAwaiter(this string s)
{
return new SubstringAwaiter(s, s.Length / 2, s.Length - s.Length / 2);
}
}
public class SubstringAwaiter
{
private readonly string _value;
private readonly int _start;
private readonly int _length;
private string _result;
private Action _continuation;
public SubstringAwaiter(string value, int start, int length)
{
_value = value;
_start = start;
_length = length;
}
public bool IsCompleted { get; private set; }
public void OnCompleted(Action callback)
{
if (callback == null)
return;
_continuation += callback;
}
public string GetResult()
{
if (!IsCompleted)
throw new InvalidOperationException();
return _result;
}
public void Execute()
{
_result = _value.Substring(_start, _length);
IsCompleted = true;
if (_continuation != null)
_continuation();
}
}
public class Program
{
public static void Main()
{
var awaiter = "HelloWorld".GetAwaiter();
awaiter.OnCompleted(() => Console.WriteLine(awaiter.GetResult()));
awaiter.Execute();
}
}