【问题标题】:Wrapping Object into a Task<object> only when needed仅在需要时将 Object 包装到 Task<object> 中
【发布时间】:2017-03-04 01:58:02
【问题描述】:

我正在扩展现有的 Rpc 库以支持异步方法。

在当前的实现中,我使用工厂方法来构建类型化委托,就像这样(我省略了一些与问题无关的实现细节):

    public static Func<ServiceType, PayloadType, object> BuildServiceMethodInvocation<ServiceType>(MethodInfo serviceMethod)
    {
        return (service, payload) =>
        {
            try
            {
                // payload is mapped onto an object[]
                var parameters = ReadPayload(payload)
                var result = serviceMethod.Invoke(service, parameters);

                return result;
            }
            catch (TargetInvocationException e)
            {
                // bla bla bla handle the error
                throw;
            }
        };
    }

然后将生成的object 传递给序列化类,该类将处理每种情况。

现在我还想支持异步方法,即返回 Task&lt;T&gt; 的方法。

我想将BuildServiceMethodInvocation 的签名更改为Func&lt;ServiceType, PayloadType, Task&lt;object&gt;&gt;

我不知道如何轻松地将每个可能的值包装在 Task&lt;object&gt; 中:它应该处理普通值,但也可以处理 Task&lt;T&gt;(装箱到对象中)。

有什么想法吗?

编辑: serviceMethod 可以返回一个字符串,我想得到一个 Task&lt;object&gt; 返回字符串值。 但是 serviceMethod 可以返回一个Task&lt;int&gt;,在这种情况下,我想返回一个返回 int 值(装箱到对象中)的Task&lt;object&gt;

【问题讨论】:

  • 如果不需要异步:使用 Task.FromResult(yourObject).
  • AFAICT,Task.FromResult 不会包装 Task 类型的结果。
  • 为什么需要包装Task?可以退货吗?
  • 外部代码必须等待任务。我怎么知道如何等待?我该如何退货?看看我的编辑,如果它可以更好地解释问题。
  • 好的,我明白你的意思了。请参阅此处了解如何执行此操作:stackoverflow.com/a/15530170/5311735

标签: c# async-await


【解决方案1】:

使其在语法上工作的最简单方法是Task.FromResult。现在,这不会使您的方法异步。但是没有额外的装箱或拆箱,至少不会比以前更多。

public static Func<ServiceType, PayloadType, Task> BuildServiceMethodInvocation<ServiceType>(MethodInfo serviceMethod)
{
    return (service, payload) =>
    {
        try
        {
            // payload is mapped onto an object[]
            var parameters = ReadPayload(payload)
            var result = serviceMethod.Invoke(service, parameters);

            // forward the task if it already *is* a task
            var task = (result as Task) ?? Task.FromResult(result);

            return task;
        }
        catch (TargetInvocationException e)
        {
            // bla bla bla handle the error
            throw;
        }
    };
}

【讨论】:

  • 我不认为这会正确地处理 Task 类型结果的情况。请参阅编辑。
  • @A.Chiesa 恐怕我不明白。您的 Invoke 返回一个对象。 FromResult 接受一个对象。您认为哪些地方可能处理不当?
  • @A.Chiesa 如果您的 Invoke 已经返回一个任务,我已经添加了一行来使用现有的任务。
  • 您阅读我的编辑了吗?如果结果是 Task&lt;int&gt; 我想得到一个 Task&lt;object&gt; 返回 int 值,而不是包装的任务。
  • 也许我错了,但如果我返回 Task 而不是 Task&lt;object&gt; 如何读取任务结果,以便稍后序列化? Task 不就是一个void返回方法吗?
【解决方案2】:

好的,将 @Evk(以及 John Skeet 和 Marc Gravell)的提示汇总在一起,我想出了一个似乎可行的解决方案。

调用以这种方式更改:

public static Func<ServiceType, PayloadType, object> BuildServiceMethodInvocation<ServiceType>(MethodInfo serviceMethod)
{
    var taskWrappingFunc = BuildTaskWrapperFunction(serviceMethod.ReturnType);

    return (service, payload) =>
    {
        try
        {
            // payload is mapped onto an object[]
            var parameters = ReadPayload(payload)
            var result = serviceMethod.Invoke(service, parameters);

            return taskWrappingFunc(result);
        }
        catch (TargetInvocationException e)
        {
            // bla bla bla handle the error
            throw;
        }
    };
}

BuildTaskWrapperFunction 负责构建一个函数,该函数将接受一个对象并返回一个Task&lt;object&gt;,实际上是根据需要提取和重新装箱结果。

    public static Func<object, Task<object>> BuildTaskWrapperFunction(Type returnType)
    {
        // manage Task<T> types
        Type taskResultType;
        if (IsATaskOfT(returnType, out taskResultType))
        {
            var f = MakeTaskWrappingMethodInfo.MakeGenericMethod(returnType);
            return (Func<object, Task<object>>)f.Invoke(null, new object[0]);
        } 

        // Manage Task
        if (typeof(Task).IsAssignableFrom(returnType)) return WrapBaseTask;

        // everything else: just wrap the synchronous result.
        return obj => Task.FromResult(obj);
    }

    // A Task is waited and then null is returned.
    // questionable, but it's ok for my scenario.
    private static async Task<object> WrapBaseTask(object obj)
    {
        var task = (Task) obj;
        if (task == null) throw new InvalidOperationException("The returned Task instance cannot be null.");
        await task;
        return null;
    }

    /// <summary> This method just returns a func that awaits for the typed task to complete
    /// and returns the result as a boxed object. </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static Func<object, Task<object>> WrapTypedTask<T>()
    {
        return async obj => await (Task<T>)obj;
    }

    private static readonly Type TypeOfTask = typeof(Task<>);

    /// <summary> Returns true if the provided type is a Task&lt;T&gt; or
    /// extends it. </summary>
    /// <param name="type"></param>
    /// <param name="taskResultType">The type of the result of the Task.</param>
    /// <returns></returns>
    public static bool IsATaskOfT(Type type, out Type taskResultType)
    {
        while (type != null)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == TypeOfTask)
            {
                taskResultType = type.GetGenericArguments()[0];
                return true;
            }

            type = type.BaseType;
        }

        taskResultType = null;
        return false;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 2017-05-11
    • 2017-12-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    相关资源
    最近更新 更多