我不完全清楚你的意图是什么,因为代码不清楚。您正在寻找一个GetAwaiter()
方法的返回类型,但当然还有其他类型Task
有这个方法的。此外,您的代码将错过通过扩展方法等待的内容。
如果您打算假设该函数返回一个任务对象(代码当前确实这样做),那么您应该只检查它而不是GetAwaiter()
方法。相反,您应该只调用GetAwaiter()
动态地调用方法,以便容纳该方法的任何内容。
就个人而言,如果不会经常调用此代码,我会使用dynamic
, 尝试调用GetAwaiter()
,如果失败(因为该方法不存在),则捕获异常并直接调用委托。如果性能很重要,您可以记住类型到等待者的状态,以便在您点击一次后可以跳过异常。请注意,使用dynamic
,您将适应大多数可等待的场景(它仍然找不到扩展方法GetAwaiter()
s).
这是一个例子:
private static readonly HashSet<MethodInfo> _notAwaitable = new HashSet<MethodInfo>();
public static async Task<object> GetFuncResult(string funcName)
{
Func<object> func = _savedFuncs[funcName];
dynamic result = func();
if (!_notAwaitable.Contains(func.Method))
{
try
{
return await result;
}
catch (RuntimeBinderException) { } // not awaitable
_notAwaitable.Add(func.Method);
}
return result;
}
这应该可以满足您的要求,并且还应该具有高性能。这dynamic
运行时支持已经缓存了可等待场景的解决方案,并通过存储不可等待的场景MethodInfo
哈希集中的实例,代码避免了遭受RuntimeBinderException
对于任何给定的委托目标方法多次。
一旦代码“预热”(即以后续传递的方式调用),它就不应该成为瓶颈。
请注意,上面的实现假设您是not使用多播委托。考虑到上下文,这似乎是一个合理的假设,因为没有对可等待的多播委托的内置语言支持(或者更确切地说,它会起作用,但运行时中没有任何东西可以解决关于which可等待的正在等待)。但如果您确实需要的话,您当然可以扩展上述内容以支持多播委托。
如果您不关心支持所有可等待的场景,而只是Task
基于 - 的,你可以像这样简化上面的内容:
public static async Task<object> GetFuncResult(string funcName)
{
Func<object> func = _savedFuncs[funcName];
object result = func();
if (result is Task task)
{
await task;
return ((dynamic)task).Result;
}
return result;
}
在这里,类型检查Task
用于代替哈希集。再次,dynamic
运行时支持将缓存Result
与此方法一起使用的每种类型的任务的访问器,因此一旦预热,将与任何其他面向动态的解决方案一样执行。
最后,请注意,如果您有Func<Task>
,上面的方法不起作用,因为它假设所有Task
对象有一个有效的结果。有人可能会说,考虑到这种歧义,最好一开始就不要在字典中填充类似的内容。但假设这种情况令人担忧,则可以修改上述内容以考虑这种可能性:
public static async Task<object> GetFuncResult(string funcName)
{
Func<object> func = _savedFuncs[funcName];
object result = func();
if (result is Task task)
{
Type resultType = result.GetType();
// Some non-result task scenarios return Task<VoidTaskResult> instead
// of a plain non-generic Task, so check for both.
if (resultType != typeof(Task) &&
resultType.GenericTypeArguments[0].FullName != "System.Threading.Tasks.VoidTaskResult")
{
await task;
return ((dynamic)task).Result;
}
}
return result;
}
不幸的是,因为在某些情况下编译器使用Task<VoidTaskResult>
而不是非通用的Task
类型,仅检查是不够的Task
。另外,因为VoidTaskResult
不是公共类型,代码必须检查类型名称是否为string
值而不是typeof(Task<VoidTaskResult>)
。所以,有点尴尬。但它将解决返回的东西是Task
本身,而不是任务的结果。
当然,GetAwaiter()
方法不存在这个问题。因此,如果这确实令人担忧,那么这将是选择GetAwaiter()
方法而不是is Task
方法。