Как написать асинхронный метод с параметром out?



Я хочу написать асинхронный метод с , например:



public async void Method1()
{
int op;
int result = await GetDataTaskAsync(out op);
}


как мне это сделать в GetDataTaskAsync?

599   7  

7 ответов:

вы не можете иметь асинхронные методы с ref или out параметры.

Lucian Wischik объясняет, почему это невозможно в этом потоке MSDN: http://social.msdn.microsoft.com/Forums/en-US/d2f48a52-e35a-4948-844d-828a1a6deb74/why-async-methods-cannot-have-ref-or-out-parameters

почему асинхронные методы не поддерживают параметры out-by-reference? (или параметры ref?) Это ограничение среды CLR. Мы решили реализовать асинхронность методы аналогично методам итератора -- т. е. через компилятор, преобразующий метод в a состояние-машина-объект. Среда CLR не имеет безопасного способа хранения адреса "out параметр "или" reference параметр " как поле объекта. Единственным способом поддержки параметров out-by-reference было бы, если асинхронная функция была выполнена низкоуровневой перезаписью среды CLR вместо a компилятор-переписать. Мы изучили этот подход, и у него было много работы для него, но это будет в конечном итоге были настолько дорогостоящими, что это никогда не будет произошло.

типичный обходной путь для этой ситуации должен иметь асинхронный метод, возвращающий Кортеж вместо этого. Вы можете переписать свой метод как таковой:

public async void Method1()
{
    var tuple = await GetDataTaskAsync();
    int op = tuple.Item1;
    int result = tuple.Item2;
}

public async Task<Tuple<int, int>> GetDataTaskAsync()
{
    //...
    return new Tuple<int, int>(1, 2);
}

вы не можете иметь ref или out параметры async методы (как уже отмечалось).

это кричит для некоторого моделирования в данных, движущихся вокруг:

public class Data
{
    public int Op {get; set;}
    public int Result {get; set;}
}

public async void Method1()
{
    Data data = await GetDataTaskAsync();
    // use data.Op and data.Result from here on
}

public async Task<Data> GetDataTaskAsync()
{
    var returnValue = new Data();
    // Fill up returnValue
    return returnValue;
}

вы получаете возможность более легко использовать свой код, а также он более удобочитаем, чем переменные или кортежи.

одна хорошая особенность out параметры-это то, что они могут использоваться для возврата данных, даже если функция создает исключение. Я думаю, что ближайший эквивалент делает это с async метод будет использовать новый объект для хранения данных, что и async метод и вызывающий может ссылаться. Другим способом было бы передайте делегат, как предложено в другом ответе.

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

вот пример реализации с использованием общего объекта для имитации ref и out для использования с async методы и другие различные сценарии, где ref и out недоступны:

class Ref<T>
{
    // Field rather than a property to support passing to functions
    // accepting `ref T` or `out T`.
    public T Value;
}

async Task OperationExampleAsync(Ref<int> successfulLoopsRef)
{
    var things = new[] { 0, 1, 2, };
    var i = 0;
    while (true)
    {
        // Fourth iteration will throw an exception, but we will still have
        // communicated data back to the caller via successfulLoopsRef.
        things[i] += i;
        successfulLoopsRef.Value++;
        i++;
    }
}

async Task UsageExample()
{
    var successCounterRef = new Ref<int>();
    // Note that it does not make sense to access successCounterRef
    // until OperationExampleAsync completes (either fails or succeeds)
    // because there’s no synchronization. Here, I think of passing
    // the variable as “temporarily giving ownership” of the referenced
    // object to OperationExampleAsync. Deciding on conventions is up to
    // you and belongs in documentation ^^.
    try
    {
        await OperationExampleAsync(successCounterRef);
    }
    finally
    {
        Console.WriteLine($"Had {successCounterRef.Value} successful loops.");
    }
}

Алекс сделал большое замечание о читаемости. Эквивалентно, функция также является интерфейсом, достаточным для определения возвращаемых типов, и вы также получаете значимые имена переменных.

delegate void OpDelegate(int op);
Task<bool> GetDataTaskAsync(OpDelegate callback)
{
    bool canGetData = true;
    if (canGetData) callback(5);
    return Task.FromResult(canGetData);
}

вызывающие объекты предоставляют лямбда (или именованную функцию), и intellisense помогает, копируя имена переменных из делегата.

int myOp;
bool result = await GetDataTaskAsync(op => myOp = op);

этот конкретный подход похож на метод "Try", где myOp устанавливается, если результат метода true. В противном случае, вы не заботитесь о myOp.

Решение C#7+ использовать неявный синтаксис кортежей.

    private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request)
    { 
        return (true, BadRequest(new OpenIdErrorResponse
        {
            Error = OpenIdConnectConstants.Errors.AccessDenied,
            ErrorDescription = "Access token provided is not valid."
        }));
    }

возвращаемый результат использует имена свойств, определенные сигнатурой метода. например:

var foo = await TryLogin(request);
if (foo.IsSuccess)
     return foo.Result;

Я думаю, что использование ValueTuples, как это может работать. Вы должны сначала добавить пакет ValueTuple NuGet:

public async void Method1()
{
    (int op, int result) tuple = await GetDataTaskAsync();
    int op = tuple.op;
    int result = tuple.result;
}

public async Task<(int op, int result)> GetDataTaskAsync()
{
    int x = 5;
    int y = 10;
    return (op: x, result: y):
}

вот код ответа @dcastro, измененный для C# 7.0 с именованными кортежами и деконструкцией кортежей, который упрощает обозначение:

public async void Method1()
{
    // Version 1, named tuples:
    // just to show how it works
    /*
    var tuple = await GetDataTaskAsync();
    int op = tuple.paramOp;
    int result = tuple.paramResult;
    */

    // Version 2, tuple deconstruction:
    // much shorter, most elegant
    (int op, int result) = await GetDataTaskAsync();
}

public async Task<(int paramOp, int paramResult)> GetDataTaskAsync()
{
    //...
    return (1, 2);
}

дополнительные сведения о новых именованных кортежах, литералах кортежей и деконструкциях кортежей см. В разделе: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/

Comments

    Ничего не найдено.