views:

139

answers:

6

Hi All Sorry if this is a stupid noob question please be gentle with me I'm trying to learn...

I want to test against the attribute methods of things like models and controllers. Mostly to make sure they have the right attrbute ie Required. But i'm also using this as an experiment with extension methods and Lambdas.

What I'd like is a method that when implimented looks some thing like

Controller controller = new Controller();
controller.MethodName(params).HasAttribute<AttributeName>();

Iveused extension methods a little but not to this degree.. I'm sure this should be simple enough to do but cant seem to get my generics etc correct.

A: 

You're looking for the Reflection class - it allows you to examine a passed object.

But frankly other than a learning exercise this is what Interfaces exist for, if you want there to be a field called Required in a model, or a method called IsRequired in a controller than implement in an interface and require that interface be implemented.

Specifically for attributes see here:

Type t = something;
System.Console.WriteLine("Author information for {0}", t);
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t);  // reflection

foreach (System.Attribute attr in attrs)
{
    if (attr is Author)
    {
        Author a = (Author)attr;
            System.Console.WriteLine("   {0}, version {1:f}", a.GetName(), a.version);
    }
}
Chris
Cheers for the link I'm looking into reflection now.I see you're point about it being a learning exercise but I don't want to clutter my solution with unnecessary Interfaces. This is more a unit testing exercise. I know it's probably not the best of approaches but I seem to like the Idea of checking for Things like Authorised etc.
Andyroo
A: 

You can check whether a method has a particular attribute by doing this:

typeof(Controller).GetMethod("MethodName").GetAttributes<AttributeName>().Any();

In terms of the extension method itself, provided that you're looking for an extension method on the Controller type, how about something like this:

public static bool HasAttribute<A>(this Controller controller, string methodName)
{
   return controller.GetType().GetMethod(methodName).GetCustomAttributes(typeof(A), true).Any();
}

Remember, extension methods are invoked on an instance, so to use this you still need an instance of Controller:

var controller = new Controller();
Assert.IsTrue(controller.HasAttribute<AttributeName>("Method1"));
theburningmonk
Nice I like that and Ryan's method. Think I might be able to cobble something together from these/
Andyroo
+1  A: 

I don't think you'll be able to implement that exactly as you described. The MethodName(params) part of your statement will actually execute the method, returning whatever the method returns, and not information about the method.

What you want to do is pass in a MethodInfo into your extension class, using reflection. So instead of your example, you'd probably end up with something like:

 controller.GetType().GetMethod(methodName).HasAttribute<AttributeName>();

You could probably simplify this down to a single extension method on the controller class by encapsulating GetType().GetMethod(), like so:

 controller.MethodHasAttribute(methodName, attributeName);
Ryan Brunner
Cheers Ryan I Like that approach and thanks for explaining about the execution D'oh had never thought about that bit.
Andyroo
+1  A: 

Can't quite do exactly that with extension methods, but this is close:

public static class Program
{
  static void Main(string[] args)
  {
     Controller c1 = new Controller();
     Action a1 = c1.Method1;
     Console.WriteLine(a1.HasAttribute<Controller.TestAttribute>());
  }

  public static bool HasAttribute<T>(this Action method)
  {
     return method.Method.GetCustomAttributes(typeof(T), false).Any();
  }
}

class Controller
{
  [AttributeUsage(AttributeTargets.Method)]
  public class TestAttribute : System.Attribute
  {
  }

  [Test()]
  public void Method1()
  {
  }
}
BlueMonkMN
+7  A: 

Perhaps you are looking for this:

Controller controller = new Controller();
bool ok = controller.GetMethod(c => c.MethodName(null, null))
    .HasAttribute<AttributeName>();

What's nice about writing it like this is that you have fully compile time support. All other solutions thus far use string literals to define the methods.

Here are the implementations of the GetMethod and HasAttribute<T> extension methods:

public static MethodInfo GetMethod<T>(this T instance,
    Expression<Func<T, object>> methodSelector)
{
    // Note: this is a bit simplistic implementation. It will
    // not work for all expressions.
    return ((MethodCallExpression)methodSelector.Body).Method;
}

public static MethodInfo GetMethod<T>(this T instance,
    Expression<Action<T>> methodSelector)
{
    return ((MethodCallExpression)methodSelector.Body).Method;
}

public static bool HasAttribute<TAttribute>(this MemberInfo member) 
    where TAttribute : Attribute
{
    var attributes = 
        member.GetCustomAttributes(typeof(TAttribute), true);

    return attributes.Length > 0;
}
Steven
+1 If this code is in production, I'd definitely want compile time checking.
Jerod Houghtelling
Worked a treat Thanks Steven.ps @ Jerod why the compile time checking for production. Just curious. No the code is not in production. Do you mean not having magic strings like some of the other approaches?
Andyroo
nice! I like it! :-D
theburningmonk
Compile time checking is useful for when name of the method is changed. The call to HasAttribute will have to be changed when the method name changes. If you use a 'magic' string, you will get an excpetion at runtime if the string is not changed. In a unit test environment it's not as big of a deal. I treat my unit test code as the same as production. I feel it not a second class citizen and shouldn't be treated that way. If you look at my answer I check to make sure the method is there and then give the user more information. Without that check it would have been a NullReferenceException.
Jerod Houghtelling
Cool, Thanks for the Explanation Jerod. I agree completely Unit Test could should be a first class citizen. Yeah I'm not a fan of 'magic' strings which is why I like Lambda and generic approaches to things. Unfortunately I only have a small amount of experience and struggle with some of the advanced topics (and basic for that matter). Once agin thanks to you and everybody for the help.
Andyroo
@Jarod: Unit testing and compile time support are not mutually exclusive. I usually test the heck out my (production) systems. But I also want to be able to easily refactor my code and keep away from magic strings (also in my ASP.NET markup) as much as I can. This way I prevent my tests from failing in the first place. It saves me a lot of time.
Steven
@Steven I hope I wasn't suggesting that the compile time checking was a bad thing. It's exactly how I would do it in the products that I work with.
Jerod Houghtelling
A: 

Usage:

bool hasAttribute = controller.HasMethodAttribute<TestAttribute>( "Test" )

Extension:

public static bool HasMethodAttribute<TAttribute>( this object obj, string methodName )
{
    Type type = obj.GetType();

    MethodInfo method = type.GetMethod( methodName );
    if( method == null )
    {
        throw new ArgumentException( string.Format( 
            "Method '{0}' not found on object '{1}'", methodName, type.Name ) );
    }

    return method.GetCustomAttributes( typeof( TAttribute ), true ).Length > 0 ;
} 
Jerod Houghtelling
Why the downvote? Please explain!
Jerod Houghtelling