аргументы ref и out в асинхронном методе

кто-нибудь знает, почему async методы не могут иметь ref и out аргументы? Я сделал немного или исследование об этом, но единственное, что я мог найти, это то, что это связано с развертыванием стека.

2 ответов


кто-нибудь знает, почему асинхронным методам не разрешено иметь аргументы ref и out?

конечно. Подумайте об этом - асинхронный метод обычно возвращает почти сразу, задолго до того, как большая часть фактической логики будет выполнена... это делается асинхронно. Так любой out параметры должны быть назначены до первого await выражение, и вполне возможно, должно быть какое-то ограничение на ref параметры, чтобы остановить их от используется после первого await выражения в любом случае, так как после этого они могут даже не быть действительным.

рассмотрите возможность вызова асинхронного метода с помощью out и ref параметры, используя локальные переменные для Аргументов:

int x;
int y = 10;
FooAsync(out x, ref y);

после FooAsync returns, сам метод может вернуться-так что эти локальные переменные больше не будут логически существовать... но асинхронный метод все равно сможет эффективно использовать их в своих продолжениях. Наибольшая проблема. Компилятор может создайте новый класс для захвата переменной так же, как для лямбда-выражений, но это вызовет другие проблемы... помимо всего прочего, у вас может быть местные переменная изменяется в произвольных точках с помощью метода, когда продолжения выполняются в другом потоке. По меньшей мере странно.

в принципе, нет смысла использовать out и ref параметры async методы, из-за сроков. Используйте возвращаемый тип, который включает все данные, которые вас интересуют.

если вас интересует только out и ref изменение параметров перед первым await выражение, вы всегда можете разделить метод на два:

public Task<string> FooAsync(out int x, ref int y)
{
    // Assign a value to x here, maybe change y
    return FooAsyncImpl(x, y);
}

private async Task<string> FooAsyncImpl(int x, int y) // Not ref or out!
{
}

EDIT: было бы возможно иметь out параметры с помощью Task<T> и назначьте значение непосредственно в методе так же, как возвращаемые значения. Это было бы немного странно, и это не сработало бы для ref параметры.


C# компилируется в CIL, и CIL не поддерживает это.

CIL не имеет async нативно. async методы компилируются в класс, и все (используемые) параметры и локальные переменные хранятся в полях класса, так что при вызове определенного метода этого класса код знает, где продолжить выполнение и какие значения имеют переменные.

ref и out параметры реализуются с помощью управляемых указателей и полей класса управляемых указатель тип не разрешен, поэтому компилятор не может сохранить переданную ссылку.

это ограничение на управляемые указатели в полях класса предотвращает некоторый бессмысленный код, как объяснено в ответе Джона Скита, поскольку управляемый указатель в поле класса может ссылаться на локальную переменную функции, которая уже вернулась. Однако это ограничение настолько строго, что даже безопасное и правильное использование отклоняется. The ref/out поля мог бы работа, если они ссылались в другое поле класса, и компилятор всегда обертывал локальные переменные, переданные с ref/out в классе (как он уже знает, как это сделать).

таким образом, C# просто не имеет никакого способа обойти ограничения, введенные CIL. Даже если дизайнеры C# хотят разрешить это (я не говорю, что они это делают), они не могут.