Как проверить, есть ли у метода атрибут

У меня есть пример класса

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

будет работать для Method3. Теперь, как я могу сделать этот универсальный метод таким образом, чтобы он также принимал строки и классы в качестве параметров?


person 4rchie    schedule 11.01.2012    source источник
comment
Вы хотите проверить, есть ли у класса, метода или свойства такой атрибут в одном универсальном методе?   -  person Anatolii Gabuza    schedule 11.01.2012
comment
Вы просто хотите знать, применен ли к методу атрибут определенного типа?   -  person Rajesh    schedule 11.01.2012
comment
Я хочу проверить, установлен ли у метода определенный атрибут, но я не хочу передавать имя метода в виде строки.   -  person 4rchie    schedule 11.01.2012
comment
Этому не хватает производительности? Отражение для каждого входящего API-интерфейса - дорогое удовольствие, не так ли?   -  person Jacob Roberts    schedule 13.05.2021


Ответы (5)


Проблема с вашим кодом - это подпись 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;
}

Вы можете использовать его так, но его также можно использовать с любым методом:

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, FieldInfo и 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.

person Johannes Rudolph    schedule 11.01.2012
comment
теперь я могу выполнить MethodHasAuthorizeAttribute (() = ›Controller.AjaxDashboardNodes (по умолчанию (TreeViewItem))) Думаю, я мог бы жить с этими дополнительными символами :) - person 4rchie; 11.01.2012
comment
я создал новый вопрос из этого ответа, спрашивая, как обобщить решение: stackoverflow.com/questions/41493289/ - person Alex Gordon; 05.01.2017

Для текущей версии .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, предоставленный другими ответами здесь.

person Mafii    schedule 16.09.2016

Я делаю что-то подобное:

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);
person Rafal T    schedule 09.07.2013

Если вы используете 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>();
person Orlando Helmer    schedule 24.07.2020

Найдите пример, в котором я нахожу методы в классе, к которым применен указанный атрибут.

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

Переданный параметр является экземпляром класса. Вы можете изменить код в соответствии с вашими требованиями, что должно быть довольно легко.

person Rajesh    schedule 11.01.2012