سؤال

Is it possible to use a TPL Task<TResult> to asynchronously invoke a thread-safe method with the following signature and retrieve the boolean return value and the output parameter?

public bool TryGet(T1 criteria,
                   out T2 output)

Obviously I can't use a lambda expression because of the output parameter. Additionally, I cannot solve the problem by defining a custom delegate such as below and passing that to the Task<TResult> constructor as I need to pass the criteria as a strongly typed parameter which the constructor does not support.

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

Is the best option to write a wrapper such as below and invoke that asynchronously instead?

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

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

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

Just seems a bit inelegant and has a bit of a whiff about it.

هل كانت مفيدة؟

المحلول

This is something I've also wrestled with.

I came up with a similar solution, except rather than use a Tuple I wrote a simple wrapper class, just to make things a bit more readable.

I'd also be interested to see any better solution - but what you propose seems as good as anything I came up with.

Here's what my wrapper class and its usage looks like. This is not an answer to your question; just a suggestion to (perhaps) make your solution a bit more readable.

(Although I concede that the Task<TryResult<DateTime>> declaration itself might not be considered all that readable!)

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;
    }
}

نصائح أخرى

I think your approach is pretty much the best you can do. If you are doing this often, you could use a helper method that convert a delegate with out parameter to a Tuple-returning delegate (or something like TryResult-returning, as in Matthew Watson's answer):

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);
    };
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top