我正在扩展现有的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<T>
。
我想更改BuildServiceMethodInvocation
的签名,使其成为Func<ServiceType, PayloadType, Task<object>>
。
我不知道如何轻松地在Task<object>
中包装每个可能的值:它应该处理普通值,还要处理Task<T>
(装入对象)。
有什么想法吗?
编辑:
serviceMethod可以返回一个字符串,我希望得到Task<object>
返回字符串值。
但是serviceMethod可以返回Task<int>
,在这种情况下,我想返回Task<object>
返回int值(装入对象)。
答案 0 :(得分: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;
}
};
}
答案 1 :(得分:0)
好的,将@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<object>
,实际上根据需要提取和重新装箱结果。
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<T> 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;
}