Асинхронный вызов метода с выходным параметром

Можно ли использовать TPL Task<TResult> асинхронно вызвать потокобезопасный метод со следующей сигнатурой и получить логическое возвращаемое значение и выходной параметр?

public bool TryGet(T1 criteria,
                   out T2 output)

Очевидно, я не могу использовать лямбда-выражение из-за выходного параметра. Кроме того, я не могу решить проблему, определив пользовательский делегат, такой как ниже, и передав его Task<TResult> конструктор, поскольку мне нужно передать критерии как строго типизированный параметр, который конструктор не поддерживает.

public delegate TResult Func<T1, T2, TResult>(T1 arg1,
                                              out T2 arg2);

Является ли лучшим вариантом написать обертку, такую ​​как ниже, и вместо этого вызывать ее асинхронно?

public Tuple<bool, T2> TryGetWrapper(T1 criteria)
{
    T2 output;

    bool result = obj.TryGet(criteria,
                             out output);

    return new Tuple<bool, T2>(result,
                               output);
}

Просто кажется немного неэластичным и немного нюхает об этом.

2 ответа

Решение

Это то, с чем я тоже боролся.

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

Мне также было бы интересно найти лучшее решение, но то, что вы предлагаете, кажется таким же хорошим, как и все, что я придумал.

Вот как выглядит мой класс-оболочка и его использование. Это не ответ на ваш вопрос; просто предложение (возможно) сделать ваше решение немного более читабельным.

(Хотя я признаю, что Task<TryResult<DateTime>> Само объявление не может считаться таким читаемым!)

using System;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    internal class Program
    {
        static void Main()
        {
            string dateString = "Invalid Date";

            var tryParseDateTask = new Task<TryResult<DateTime>>(() =>
            {
                DateTime result;

                if (DateTime.TryParse(dateString, out result))
                    return TryResult<DateTime>.Success(result);
                else
                    return TryResult<DateTime>.Failure();
            });

            tryParseDateTask.Start();

            if (tryParseDateTask.Result.IsSuccessful)
                Console.WriteLine(dateString + " was parsed OK.");
            else
                Console.WriteLine(dateString + " was parsed as " + tryParseDateTask.Result.Value);
        }
    }

    public class TryResult<T>
    {
        public static TryResult<T> Success(T value)
        {
            return new TryResult<T>(value, true);
        }

        public static TryResult<T> Failure()
        {
            return new TryResult<T>(default(T), false);
        }

        TryResult(T value, bool isSuccessful)
        {
            this.value = value;
            this.isSuccessful = isSuccessful;
        }

        public T Value
        {
            get
            {
                return value;
            }
        }

        public bool IsSuccessful
        {
            get
            {
                return isSuccessful;
            }
        }

        readonly T value;
        readonly bool isSuccessful;
    }
}

Я думаю, что ваш подход - это лучшее, что вы можете сделать. Если вы делаете это часто, вы можете использовать вспомогательный метод, который преобразует делегата с out параметр к TupleВозврат делегата (или что-то вроде TryResultвозвращаясь, как в ответе Мэтью Уотсона):

public delegate TResult OutFunc<TIn, TOut, TResult>(TIn input, out TOut output);

public static Func<TIn, Tuple<TResult, TOut>> OutToTuple<TIn, TOut, TResult>(
    OutFunc<TIn, TOut, TResult> outFunc)
{
    return input =>
    {
        TOut output;
        TResult result = outFunc(input, out output);
        return Tuple.Create(result, output);
    };
}
Другие вопросы по тегам