如何检查方法是否具有属性

本文关键字:是否 属性 方法 何检查 检查 | 更新日期: 2023-09-27 18:20:10

我有一个示例类

public class MyClass{
    ActionResult Method1(){
        ....
    } 
    [Authorize]
    ActionResult Method2(){
       ....
    }
    [Authorize]    
    ActionResult Method3(int value){
       ....
    }
}

现在我想写一个返回true/false的函数,它可以像一样执行

var controller = new MyClass();
Assert.IsFalse(MethodHasAuthorizeAttribute(controller.Method1));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method2));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method3));

我已经到了

public bool MethodHasAuthorizeAttribute(Func<int, ActionResult> function)
{
    return function.Method.GetCustomAttributes(typeof(AuthorizeAttribute), false).Length > 0;
}

将为Method3工作。现在,我如何才能以字符串和类作为参数的方式实现泛型呢?

如何检查方法是否具有属性

代码的问题是public bool MethodHasAuthorizeAttribute(Func<int, ActionResult> function)的签名。MethodHasAuthorizeAttribute只能与匹配指定委托签名的参数一起使用。在这种情况下,返回具有类型为int的参数的ActionResult的方法。

当您像MethodHasAuthorizeAttribute(controller.Method3)那样调用此方法时,编译器将执行方法组转换。这可能并不总是理想的,并且可能会产生意外的结果(方法组转换并不总是straigthforward)。如果您尝试调用MethodHasAuthorizeAttribute(controller.Method1),您将得到一个编译器错误,因为没有转换。

一个更通用的解决方案可以用表达式树和著名的"MethodOf"技巧构建。它使用编译器生成的表达式树来查找调用目标:

public static MethodInfo MethodOf( Expression<System.Action> expression )
{
    MethodCallExpression body = (MethodCallExpression)expression.Body;
    return body.Method;
}

你可以这样使用它,但它也可以与任何方法一起使用:

MethodInfo method = MethodOf( () => controller.Method3( default( int ) ) );

这样,我们就可以构建一个通用的实现:

public static bool MethodHasAuthorizeAttribute( Expression<System.Action> expression )
{
    var method = MethodOf( expression );
    const bool includeInherited = false;
    return method.GetCustomAttributes( typeof( AuthorizeAttribute ), includeInherited ).Any();
}

好的,这是方法。现在,如果您想对类或字段应用Attribute检查(我将保留属性,因为它们实际上是方法),我们需要对MemberInfo执行检查,它是TypeFieldInfoMethodInfo的继承根。这就像将属性搜索提取到一个单独的方法中并提供具有漂亮名称的适当适配器方法一样简单:

public static bool MethodHasAuthorizeAttribute( Expression<System.Action> expression )
{
    MemberInfo member = MethodOf( expression );
    return MemberHasAuthorizeAttribute( member );
}
public static bool TypeHasAuthorizeAttribute( Type t)
{
    return MemberHasAuthorizeAttribute( t );
}
private static bool MemberHasAuthorizeAttribute( MemberInfo member )
{
    const bool includeInherited = false;
    return member.GetCustomAttributes( typeof( AuthorizeAttribute ), includeInherited ).Any();
}

我将把字段的实现作为练习,您可以使用与MethodOf相同的技巧。

与上面使用当前.NET/C#版本(4.6.1,C#6)的其他解决方案相比,有一个更简单的解决方案:

如果你只有一个方法的名称:

var method = typeof(TestClass).GetMethods()
  .SingleOrDefault(x => x.Name == nameof(TestClass.TestMethod));
var attribute = method?.GetCustomAttributes(typeof(MethodAttribute), true)
  .Single() as MethodAttribute;

现在检查您是否在方法上设置了属性:

bool isDefined = attribute != null;

如果你想访问属性的属性,你可以简单地这样做:

var someInfo = attribute.SomeMethodInfo

如果有多个方法具有相同的名称,您可以继续使用method.GetParameters()并检查参数,而不是.GetMethods().Single...

如果你知道你的方法没有参数,这个检查很容易:

var method = typeof(TestClass).GetMethods()
    .SingleOrDefault(
      x => x.Name == nameof(TestClass.TestMethod) 
      && x.GetParameters().Length == 0
);

如果没有,这将更加复杂(检查参数等),而其他解决方案使用起来更容易、更稳健。

因此:如果方法没有重载,或者只想从具有指定数量参数的方法中读取属性,请使用此选项。否则,请使用此处其他答案提供的MethodOf

我做了这样的事情:

public static bool MethodHasAuthorizeAttribute(this Delegate pMethod, string pRoleAccess)
{
    var mi = pMethod.GetMethodInfo();
    const bool includeInherited = false;
    var atr = mi.GetCustomAttributes(typeof(AuthorizeAttribute), includeInherited)
                .Select(t => (AuthorizeAttribute)t)
                .Where(t => pRoleAccess.Length>0?t.Roles == pRoleAccess:true);
    if (pRoleAccess == String.Empty)
    {
        return !atr.Any();
    }
    else
    {
        return atr.Any();
    }
}
public static bool MethodHasAllowAnonymousAttribute(this Delegate pMethod)
{
    var mi = pMethod.GetMethodInfo();
    const bool includeInherited = false;
    var atr = mi.GetCustomAttributes(typeof(AllowAnonymousAttribute), includeInherited);
    return atr.Any();
}

调用它遵循

Func<string,System.Web.Mvc.ActionResult> func = controller.Login;
bool atrAuthorize = func.MethodHasAuthorizeAttribute(String.Empty);

如果您使用FluentAssertions,您可以执行以下操作:

var classInstance = new MyClass();
Func<ActionResult> method1 = classInstance.Method1;
method1.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();
Func<ActionResult> method2 = classInstance.Method2;
method2.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();
Func<int, ActionResult> method3 = classInstance.Method3;
method3.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();

在类中查找应用了指定属性的方法的示例。

private static void GetMethodInfo(object className)
        {
            var methods = className.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
            foreach(var m in methods)
            {
                var parameters = m.GetParameters();
                var att = m.GetCustomAttributes(typeof (CustomAttribute), true);
            }
        }

传递的参数是一个类的实例。您可以修改代码以满足您的需求,这应该很容易。