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

4rc*_*hie 24 c# reflection tdd attributes assert

我有一个示例课程

public class MyClass{

    ActionResult Method1(){
        ....
    } 

    [Authorize]
    ActionResult Method2(){
       ....
    }

    [Authorize]    
    ActionResult Method3(int value){
       ....
    }

}
Run Code Online (Sandbox Code Playgroud)

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

var controller = new MyClass();

Assert.IsFalse(MethodHasAuthorizeAttribute(controller.Method1));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method2));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method3));
Run Code Online (Sandbox Code Playgroud)

我到了那个地步

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

适用于Method3.现在我怎么能以一种将字符串和类作为参数的方式来做那个泛型呢?

Joh*_*lph 20

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

当您调用此方法时MethodHasAuthorizeAttribute(controller.Method3),编译器将执行方法组转换.这可能并不总是需要,并且可能产生意外结果(方法组转换并不总是很好).如果您尝试调用MethodHasAuthorizeAttribute(controller.Method1),则会出现编译器错误,因为没有转换.

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

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

您可以像这样使用它,但它也可以用于任何方法:

MethodInfo method = MethodOf( () => controller.Method3( default( int ) ) );
Run Code Online (Sandbox Code Playgroud)

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

public static bool MethodHasAuthorizeAttribute( Expression<System.Action> expression )
{
    var method = MethodOf( expression );

    const bool includeInherited = false;
    return method.GetCustomAttributes( typeof( AuthorizeAttribute ), includeInherited ).Any();
}
Run Code Online (Sandbox Code Playgroud)

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

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

我将把字段的实现留作练习,你可以使用与MethodOf相同的技巧.


Maf*_*fii 6

与上面的其他解决方案相比,使用当前的.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;
Run Code Online (Sandbox Code Playgroud)

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

bool isDefined = attribute != null;
Run Code Online (Sandbox Code Playgroud)

如果要访问属性的属性,可以这样做:

var someInfo = attribute.SomeMethodInfo
Run Code Online (Sandbox Code Playgroud)

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

如果您知道您的方法没有参数,则此检查很简单:

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

如果不是,这将变得更复杂(检查参数等),并且其他解决方案使用起来更容易和更健壮.

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