В вашем примере DoSomething
компилятор не жалуется, потому что тип вашего метода MyAwaitable GetResult
не имеет ничего общего с THuh
. Утверждение, относящееся к THuh
, есть return null;
. Нулевой литерал неявно преобразуется в THuh
, так что все в порядке.
Ключевое слово IEnumerable
, аналогичное await
, называется foreach
. await
требует тип, соответствующий определенному шаблону, и foreach
тоже. Один — это механизм для использования ожидаемых типов, другой — для использования перечислимых типов.
С другой стороны, блоки итераторов (yield return
и yield break
) представляют собой механизмы для определения перечислимых типов (путем написания метода, а не явного объявления типа). Аналогия здесь — ключевое слово async
.
Чтобы уточнить аналогию между async
и yield return
, обратите внимание, что блок итератора, возвращающий IEnumerable<int>
, может содержать оператор yield return 42;
, а асинхронный метод, возвращающий Task<int>
, может содержать оператор yield return 42;
. Обратите внимание, что в обоих случаях тип возвращаемого выражения является не типом возвращаемого значения метода, а скорее аргументом типа возвращаемого типа метода.
Если вы еще этого не сделали, вам действительно следует прочитать блог Эрика Липперта по этим темам:
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/
Наконец, примеры см. в сообщении в блоге Эрика со ссылкой на его статья MSDN и связанные статьи в том же выпуске и последующая статья Билла Вагнера по адресу http://msdn.microsoft.com/en-us/vstudio/hh533273
ИЗМЕНИТЬ
Я вижу некоторые образцы, которые, кажется, не следуют этому правилу — возвращаемое значение кажется достижимым, а значение не по умолчанию. Это значение недоступно? Если да, то почему неудобный недоступный оператор возврата?
Фраза «конечная точка тела должна быть недоступна» означает, что у вас должен быть оператор возврата. Конечная точка тела находится после оператора return и становится недостижимой с помощью оператора return. Пример использования обычного метода int-return:
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();
}
}
person
phoog
schedule
08.12.2011