如何通过反射判断C#方法是否异步/等待?

Isa*_*ham 34 c# reflection asynchronous

例如

class Foo { public async Task Bar() { await Task.Delay(500); } }
Run Code Online (Sandbox Code Playgroud)

如果我们反思这个类和方法,我如何确定这是否是一个真正的async/await方法,而不仅仅是一个碰巧返回任务的方法?

class Foo { public Task Bar() { return Task.Delay(500); } }
Run Code Online (Sandbox Code Playgroud)

Raw*_*ing 46

在我的代码副本中,MethodInfofor async方法在CustomAttributes属性中包含以下项:

  • 一个 DebuggerStepThroughAttribute
  • 一个 AsyncStateMachineAttribute

MethodInfo普通方法的属性中包含任何项目CustomAttributes.

似乎AsyncStateMachineAttribute 应该可靠地找到async方法而不是标准方法.

编辑:事实上,该页面甚至在示例中有以下内容!

如以下示例所示,您可以确定方法是使用Async(Visual Basic)还是async(C#Reference)修饰符进行标记.在该示例中,IsAsyncMethod执行以下步骤:

  • 使用Type.GetMethod获取方法名称的MethodInfo对象.

  • 通过使用GetType运算符(Visual Basic)或typeof(C#引用)获取属性的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);
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ter 9

Damien_The_Unbeliever提出了一个有趣的挑战。我认为检查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);
}
Run Code Online (Sandbox Code Playgroud)

编辑:检查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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管这并不是所要问的,但这是一个很好的观点。知道结果是否可以等待,而不是将该方法是否标记为“异步”,通常更有用。 (2认同)

Dam*_*ver 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);
    }
Run Code Online (Sandbox Code Playgroud)

在手动波中,它们都具有完全相同的运行时行为 - 在20秒内它们什么都不做(并且在此期间不保留线程)然后它们运行一个小委托,它只传递最初的值传递给方法.

然而,你会对待一个与另一个完全不同的因为我选择使用编译器隐藏一些管道?