Sunday, November 18, 2012

Listing methods tagged with custom attribute

Attributes are very powerful mechanism in .NET. In this particular case, I had created custom attribute and wanted to list all methods tagged with it. Additionally my requirement was to get only methods which has been tagged with my custom attribute with specified parameter value. Let's present this on the code sample. First, I have defined my MagicNumberAttribute attribue. It contains only one propery, MagicNumber, which value will be used later to select certain methods.

[AttributeUsage(AttributeTargets.All, AllowMultiple=false)]
public class MagicNumberAttribute : Attribute
{
    public int MagicNumber { get; private set; }

 public MagicNumberAttribute(int magicNumber)
    {
        MagicNumber = magicNumber;
    }
}


Assuming that I have class, with methods tagged with my MagicNumberAttribute

public class MethodsToList
{
 [MagicNumberAttribute(2)]
 public void MethodA()
 {
  ...
 }
 
 [MagicNumberAttribute(3)]
 public void MethodB()
 {
  ...
 }
 
 [MagicNumberAttribute(7)]
 public void MethodC()
 {
  ...
 }
 
 public void MethodD()
 {
  ...
 }
}


what would be the code which could allow me to get only tagged methods with MagicNumber value greater than 2 ? This is fairly simple First we are getting all methods from type (in our case it will be MethodsToList).

var methods = type.GetMethods();


In the next step we are getting only those methods which are tagged with MagicNumberAttribute. This can be done using Attribute.IsDefined static method.

var methodsWithAttribute = methods.
   Where(method => Attribute.IsDefined(method, typeof(MagicNumberAttribute)));


Last step is to filter out ones which are meeting our criteria (MagicNumber value greater than 2).

methodsWithAttribute.
Where(method => getMagicNumberAttributeValue(method) > 2).ToList()


Helper function will extract for us value stored in custom attribute for each method. To achieve this we can use Attribute.GetCustomAttribute static method.

private static int getMagicNumberAttributeValue(MethodInfo methodInfo) 
{
    return ((MagicNumberAttribute)Attribute.
  GetCustomAttribute(methodInfo, typeof(MagicNumberAttribute), false)).MagicNumber;
}


Finally we just need to filter what is interesting for us using some LINQ

methodsWithAttribute.
 Where(method => getMagicNumberAttributeValue(method) > 2).ToList();


Full implementation looks like this:

public static class MagicNumberAttributeFilterer
{
 public static MethodBase GetNextActionName(Type type, MethodBase methodBase)
 {
  var methods = type.GetMethods();
  
  var methodsWithAttribute = methods.
   Where(method => Attribute.IsDefined(method, typeof(MagicNumberAttribute)));

  return methodsWithAttribute.
   Where(method => getMagicNumberAttributeValue(method) > 2).ToList();
 }

 private static int getMagicNumberAttributeValue(MethodInfo methodInfo) 
 {
    return ((MagicNumberAttribute)Attribute.
      GetCustomAttribute(methodInfo, 
                      typeof(MagicNumberAttribute), false)).MagicNumber;
 }
}


Finally to make things a bit more generic we can do something like this:

public abstract class CustomAttributeFilterer<T> where T : Attribute
{
    public IEnumerable<MethodInfo> GetMethods(Type type)
    {
        var methods = type.GetMethods();

        var methodsWithAttribute = methods.
   Where(method => Attribute.IsDefined(method, typeof(T)));

        return methodsWithAttribute.Where(hasAcceptedAttributeValue);
    }

    private bool hasAcceptedAttributeValue(MethodInfo methodInfo)
    {
        var methodAttribute = (T)Attribute.
   GetCustomAttribute(methodInfo, typeof(T), false);

        return acceptAttributeValue(methodAttribute);
    }

    protected abstract bool acceptAttributeValue(T methodAttribute);
}


And done. Now it is enough to create your own implementation based on CustomAttributeFilterer. The only thing you need to do is to implement your own rules in acceptAttributeValue.

No comments:

Post a Comment