在您的DoSomething 示例中,编译器没有抱怨,因为您的MyAwaitable 的GetResult 方法的类型与THuh 无关。与THuh 相关的语句是return null;。 null 文字可以隐式转换为THuh,所以一切都很好。
类似于await 的IEnumerable 关键字是foreach。 await 需要符合特定模式的类型,foreach 也是如此。一种是消费可等待类型的机制,另一种是消费可枚举类型的机制。
另一方面,迭代器块(yield return 和yield break)是定义可枚举类型的机制(通过编写方法而不是显式声明类型)。这里的比喻是async 关键字。
为了详细说明async 和yield return 之间的类比,请注意返回IEnumerable<int> 的迭代器块可以包含语句yield return 42;,类似地,返回Task<int> 的异步方法可以包含语句@987654346 @。请注意,在这两种情况下,返回表达式的类型不是方法的返回类型,而是方法返回类型的 type 参数。
如果您还没有这样做,那么您真的应该阅读 Eric Lippert 关于以下主题的博客:
http://blogs.msdn.com/b/ericlippert/archive/tags/Async/
http://blogs.msdn.com/b/ericlippert/archive/tags/Iterators/
此外,如果您对这个概念是新的(就像对我一样),关于 Async 系列以外的延续传递风格的帖子可能会很有用:
http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/
最后,例如,请参阅Eric's blog post linking to his MSDN article and related articles in the same issue 和 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();
}
}