web-dev-qa-db-ja.com

メソッドに属性があるかどうかを確認する方法

サンプルクラスがあります

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で機能します。では、文字列とクラスをパラメーターとしても使用するように、このジェネリックをどのように実行できますか?

27
4rchie

コードの問題は、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();
}

さて、それはメソッドです。ここで、クラスまたはフィールドの属性チェックを適用する場合(実際にはメソッドであるため、プロパティは割愛します)、MemberInfoType、およびFieldInfoの継承ルートであるMethodInfoに対してチェックを実行する必要があります。これは、属性検索を別のメソッドに抽出し、適切なアダプターメソッドにNice名を付けるのと同じくらい簡単です。

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と同じトリックを使用できます。

20

現在の.NET/C#バージョン(4.6.1、C#6)では、上記の他のソリューションと比較して、より簡単なソリューションを利用できます。

その名前のメソッドが1つしかない場合:

_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を使用してください。

5
Mafii

私はそのようなことをします:

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);
2
Rafal T

指定された属性が適用されているクラスのメソッドを見つけるサンプルをいくつか見つけます。

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

渡されるパラメーターは、クラスのインスタンスです。要件に合わせてコードを変更できますが、これは非常に簡単です。

0
Rajesh