【问题标题】:How to check if method has an attribute如何检查方法是否具有属性
【发布时间】:2012-01-11 09:37:35
【问题描述】:

我有一个示例类

public class MyClass{

    ActionResult Method1(){
        ....
    } 

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

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

}

现在我想要写一个返回真/假的函数,可以像这样执行

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;
}

适用于方法 3。现在我怎样才能以一种将字符串和类也作为参数的方式来实现这个泛型?

【问题讨论】:

  • 你想在一个泛型方法中检查类、方法或属性是否有这样的属性吗?
  • 你只是想知道一个方法是否应用了某种类型的属性?
  • 我想检查方法是否设置了特定的属性,但我不想将方法名称作为字符串传递。
  • 这是否缺乏性能?为每个 api 入口做反射很昂贵,不是吗?

标签: c# reflection attributes tdd assert


【解决方案1】:

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

当您像MethodHasAuthorizeAttribute(controller.Method3) 这样调用此方法时,编译器将进行方法组转换。这可能并不总是需要的,并且可能会产生意想不到的结果(方法组转换并不总是直截了当)。如果你尝试调用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();
}

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

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 相同的技巧。

【讨论】:

  • 现在我可以执行 MethodHasAuthorizeAttribute(() => Controller.AjaxDashboardNodes(default(TreeViewItem))) 我想我可以忍受这些额外的字符:)
  • 我从这个回复中创建了一个新问题,询问如何概括解决方案:stackoverflow.com/questions/41493289/…
【解决方案2】:

与上面的其他解决方案相比,当前的 .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

【讨论】:

    【解决方案3】:

    如果您使用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>();
    

    【讨论】:

      【解决方案4】:

      我会这样做:

      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);
      

      【讨论】:

        【解决方案5】:

        找到一些示例,我可以在其中找到应用了指定属性的类中的方法。

        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);
                    }
                }
        

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

        【讨论】:

          猜你喜欢
          • 2014-07-05
          • 2011-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-12
          相关资源
          最近更新 更多