Я просмотрел другие вопросы на StackOverflow, но ни один из других вопросов/ответов не соответствует моему случаю:
Я написал метод async
в классе, который мне нужно вызвать из переопределенного метода в другом классе, производном от класса .NET System.Management.Automation.Cmdlet
.
Обычно я называю свой метод следующим образом:
public async Task TestReadingFile()
{
using MyReader reader = new MyReader(SetupMyStream("Contoso.txt"));
await foreach (MyDataObject obj in reader.GetFrames())
{
Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
}
}
Теперь, в моем классе ProcessRecord
, переопределяющем Cmdlet
базовый класс, я не могу использовать async
. Но, конечно, я хочу дождаться результата.
Как я могу это сделать?
Я ожидал использовать что-то вроде этого:
protected override void ProcessRecord()
{
using MyReader reader = new MyReader(MyFilePath);
IAsyncEnumerator<MyDataObject> enumerator = reader.GetFrames().GetAsyncEnumerator();
while (enumerator.MoveNextAsync().GetAwaiter().GetResult())
{
MyDataObject obj = enumerator.Current;
Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
}
}
Но этот подход бросает InvalidOperationException
.
Как это сделать правильно?
Большое спасибо! Вы очень помогли. Если вы хотите превратить свой комментарий в ответ, я с удовольствием проголосую за него. — Однако я не могу использовать using
на IAsynEnumerator. Если я это сделаю, я получаю сообщение об ошибке с просьбой использовать async using
, которое я по понятным причинам не могу использовать. Как бы вы предложили использовать using
в этом случае?
Причина, по которой вы получаете InvalidOperationException
, заключается в том, что ValueTask
нельзя ожидать синхронно, в отличие от Task
. Поэтому простое решение — просто превратить его в Task
методом AsTask()
.
Перечислители являются одноразовыми, что означает, что вы должны утилизировать их после использования. Пока IEnumerator
наследует IDisposable
, IAsyncEnumerator
наследует IAsyncDisposable
. В обычной ситуации вы должны были использовать await using
, но так как метод синхронный, мы должны сами понизить эту штуку. Сначала преобразуйте это в конструкцию try-finally
, а затем синхронно ждите возвращенной задачи.
protected override void ProcessRecord()
{
using MyReader reader = new MyReader(MyFilePath);
IAsyncEnumerator<MyDataObject> enumerator = reader.GetFrames().GetAsyncEnumerator();
try
{
while (enumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult())
{
MyDataObject obj = enumerator.Current;
Debug.Print($"{reader.Progress}: {obj.GetType().Name} - {obj}");
}
}
finally
{
enumerator?.DisposeAsync().AsTask().GetAwaiter().GetResult();
}
}
1.
IAsyncEnumerator
MoveNext
возвращаетValueTask
, которое нельзя ожидать синхронно. Вместо этого вы должны вызватьMoveNextAsync().AsTask().GetAwaiter().GetResult()
. 2. Перечислитель является одноразовым, поэтому вы должны использовать ключевое словоusing
для перечислителя.