下面是我希望用于实现缓存提供程序的拦截器的代码。我需要确定将调用的方法的返回类型。当调用的方法是同步时,这非常简单。然而,我遇到的大多数方法都是异步的,它们返回一个任务。
如何使用反射来确定异步方法的返回类型?
public void Intercept(IInvocation invocation)
{
try
{
if (invocation.Method.Name.StartsWith("Retrieve"))
{
var returnType = invocation.Method.ReturnType;
if (returnType.IsGenericType &&
returnType.GetGenericTypeDefinition() == typeof (Task<>))
{
var returnTypeOfTheTask = returnType.NeedSomeHelpHere();
}
}
_circuitBreaker.Execute(invocation);
}
// ...
}
您可以使用其中一个
var returnTypeOfTheTask = returnType.GetGenericArguments()[0];
或
var returnTypeOfTheTask = returnType.GenericTypeArguments[0];
您将使用哪个主要取决于您需要支持的平台(例如,第一个选项自 .NET 2.0 以来就存在,但在 Modern UI 中不受支持,而第二个选项在 .NET 4.0 中不存在)。如果两种方法都能满足您的需求,我建议使用第二种方法。
根据上面学到的知识,我创建了一个方法,可以一直深入到类型层次结构中,直到找到不再通用的类型。
/// <summary>
/// Get the output type of the given method.
/// 1. Get the method info using reflection.
/// 2. Check if the method is an async method by calling the `IsAwait()` method.
/// 3. If the method is an async method, get the return type using the `ReturnType` property.
/// a. Check if the return type is a generic type by calling the `IsGenericType` property.
/// b. If the return type is a generic type, get the generic arguments using the `GetGenericArguments` method.
/// c. Continue repeating #3 until the return type is no longer Generic.
/// 4. If the method is not async then get the return type using the `ReturnType` property.
/// </summary>
/// <param name="runType"></param>
/// <param name="methodName"></param>
/// <param name="methodParameterFQNs"></param>
/// <returns></returns>
public static Type GetOutputType(
Type runType,
string methodName,
List<string> methodParameterFQNs)
{
Type result = null;
MethodInfo methodInfo =
GetMethodByParameterMatch(
runType, methodName, methodParameterFQNs);
if (methodInfo.IsAwait())
{
result = methodInfo.ReturnType;
if (result.IsGenericType)
{
while (result.IsGenericType)
{
Type[] genericArguments = result.GetGenericArguments();
if (genericArguments.Length > 0)
{
result = genericArguments[0];
}
}
}
else
{
result = methodInfo.ReturnType;
}
}
else
{
result = methodInfo.ReturnType;
}
return result;
}