e.g。
class Foo { public async Task Bar() { await Task.Delay(500); } }
如果我们反思这个类和方法,我怎样才能确定这是否是一个真正的异步/等待方法,而不仅仅是一个碰巧返回任务的方法?
class Foo { public Task Bar() { return Task.Delay(500); } }
答案 0 :(得分:40)
在我的代码副本中,MethodInfo
方法的async
包含CustomAttributes
属性中的以下项目:
DebuggerStepThroughAttribute
AsyncStateMachineAttribute
而普通方法的MethodInfo
在其CustomAttributes
属性中包含 no 项。
好像是AsyncStateMachineAttribute
should reliably be found on an async
method and not on a standard one。
编辑:事实上,该页面甚至在示例中有以下内容!
如以下示例所示,您可以确定方法是使用Async(Visual Basic)还是async(C#Reference)修饰符进行标记。在该示例中,IsAsyncMethod执行以下步骤:
使用Type.GetMethod获取方法名称的MethodInfo对象。
使用GetType运算符(Visual Basic)或typeof(C#Reference)获取属性的Type对象。
使用MethodInfo.GetCustomAttribute获取方法和属性类型的属性对象。如果GetCustomAttribute返回Nothing(Visual Basic)或null(C#),则该方法不包含该属性。
private static bool IsAsyncMethod(Type classType, string methodName) { // Obtain the method with the specified name. MethodInfo method = classType.GetMethod(methodName); Type attType = typeof(AsyncStateMachineAttribute); // Obtain the custom attribute for the method. // The value returned contains the StateMachineType property. // Null is returned if the attribute isn't present for the method. var attrib = (AsyncStateMachineAttribute)method.GetCustomAttribute(attType); return (attrib != null); }
答案 1 :(得分:8)
以下是两种方法的示例,我问你为什么认为应该区别对待:
public static async Task<int> M1(int value)
{
await Task.Delay(20000);
return value;
}
public static Task<int> M2(int value)
{
return Task.Delay(20000).ContinueWith<int>(_=>value);
}
在手动波中,它们都具有完全相同的运行时行为 - 在20秒内它们什么也不做(并且在此期间不保留线程)然后它们运行一个只传递值的小代理最初传递给方法。
然而,你会对待一个与另一个完全不同的因为我选择使用编译器隐藏一些管道?
答案 2 :(得分:5)
AsyncStateMachineAttribute
并不是一个充分的解决方案。最初的问题不应该是该方法是否异步。相反应该是它是否等待。如果您在返回类型上检查方法GetAwaiter()
,Damien的答案中的两个方法示例都将返回true。但是,只有标记为async
的方法才会在自定义属性集合中包含AsyncStateMachineAttribute
。
如果您想使用MethodInfo.Invoke()
来调用该方法并且您事先并不知道可能已注册到消息代理的方法,那么知道该方法是否可以接受是很重要的,是等待的。
var isAwaitable = _methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
object result = null;
if (isAwaitable)
{
result = await (dynamic)_methodInfo.Invoke(_instance, _parameterArray);
}
else
{
result = _methodInfo.Invoke(_instance, _parameterArray);
}
编辑: 最好检查MethodInfo上的返回类型。这是我修改后的代码。
var isAwaitable = _methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
object invokeResult = null;
if (isAwaitable)
{
if (_methodInfo.ReturnType.IsGenericType)
{
invokeResult = (object)await (dynamic)_methodInfo.Invoke(_instance, arguments);
}
else
{
await (Task)_methodInfo.Invoke(_instance, arguments);
}
}
else
{
if (_methodInfo.ReturnType == typeof(void))
{
_methodInfo.Invoke(_instance, arguments);
}
else
{
invokeResult = _methodInfo.Invoke(_instance, arguments);
}
}