4

I'm sure there's an answer already on the forum somewhere, but I haven't been able to find it so far. As per this example I am using anonymous methods in conjunction with a delegate in order to have different methods with the different parameters but the same return type all work as function parameters:

public delegate TestCaseResult Action();
...

[TestDescription("Test whether the target computer has teaming configured")]
public TestCaseResult TargetHasOneTeam()
{
  // do some logic here and return
  // TestCaseResult
}

[TestDescription("Test whether the target computer has the named team configured")]
public TestCaseResult TargetHasNamedTeam(string teamName)
{
  // do some logic here and return
  // TestCaseResult
}
...

public static void TestThat(TestCaseBase.Action action)
{
  TestCaseResult result = action.Invoke();

  // I want to get the value of the TestDescription attribute here
}
...

// usage
TestThat(() => TargetHasOneTeam());

TestThat(() => TargetHasNamedTeam("Adapter5"));

As you can see from the example, I'd really like to be able to get the TestDescriptionAttribute attribute from within the TestThat() function. I've already poked through the Action parameter which contains my method but haven't been able to "find" my TargetHasOneTeam() method.

Community
  • 1
  • 1
millejos
  • 301
  • 3
  • 14
  • 3
    I think this is only possible if you use a expression eg. `System.Linq.Expressions.Expression`. Then you can cast the body to a MethodCallExpression, and use the MethodInfo object inside to query for attributes... – MattDavey May 14 '12 at 22:53
  • Your delegate turns into compiler generated class, derived from something like `MulticastDelegate` that holds your method info. I think it is possible to get it with reflection. – Val Bakhtin May 14 '12 at 22:55
  • 2
    I have experimented with exactly this pattern in a similar scenario. I found it to be very fragile. fyi. – usr May 14 '12 at 22:55

4 Answers4

4

In this particular case it's essentially inaccessible. You are creating a lambda which executes the method in question. That lambda eventually results in a new method being generated which is eventually the argument of the Action delegate. That method has no relation to TargetHasOneTeam and is only apparently if you dig through the IL instructions in the body.

You could skip the lambda and do a method group conversion here.

TestThat(TargetHasOneTeam);

Now TargetHasOneTeam is being directly assigned to the delegate instance and would be visible in the Delegate::MethodInfo property.

Note: In general though this is a bad idea for precisely the problem you're encountering. The attributes on the method shouldn't affect it's ability to satisfy the delegate instantiation. I would avoid this type of inspection if possible.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Val explained how it might be done but I think I agree with you that it seems like this might not be a great idea, thanks for the explanation of what's going on behind the scenes. – millejos May 15 '12 at 16:22
4

If you change TestThat(() => TargetHasOneTeam()) (you wrapping your delegate into another action) to TestThat(TargetHasOneTeam) and change TestThat like this:

public static void TestThat(TestCaseBase.Action action)
{
  TestCaseResult result = action.Invoke();
  var attrs = action.GetInvocationList()[0].Method.GetCustomAttributes(true);

  // I want to get the value of the TestDescription attribute here
}

will give you what you need.

With expressions:

public static void TestThat(Expression<Func<TestResult>> action)
{
    var attrs = ((MethodCallExpression)action.Body).Method.GetCustomAttributes(true);

    var result = action.Compile()();
}
Val Bakhtin
  • 1,434
  • 9
  • 11
  • I understand how to do this if TargetHasOneTeam() has no parameters, but what if I want to also do this with another function, say CheckTargetTeamName(string name)? – millejos May 14 '12 at 23:18
  • 2
    Then change your TestThat(...) into TestThat(Expression>) or TestThat(Expression>) and travers through expression tree like @MattDavey suggests. To call real delegate you would need to call expression.Compile() and call compiled lambda. – Val Bakhtin May 14 '12 at 23:35
1

You can get the attribute of any member with Attribute.GetCustomAttribute. First check that the attribute is defined. For example:

public static void TestThat(TestCaseBase.Action action)
{
    TestCaseResult result = action.Invoke();
    if(System.Attribute.IsDefined(action.Method, typeof(TestDescriptionAttribute)))
    {
        var attribute = (TestDescriptionAttribute)System.Attribute.GetCustomAttribute(action.Method,
            typeof(TestDescriptionAttribute));
        Console.WriteLine(attribute.TestDescription);
    }
}
Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
0

See this example on MSDN.

class TestAuthorAttribute
{
  static void Test()
  {
    PrintAuthorInfo(typeof(FirstClass));
    PrintAuthorInfo(typeof(SecondClass));
    PrintAuthorInfo(typeof(ThirdClass));
  }

  private static void PrintAuthorInfo(System.Type t)
 {
    System.Console.WriteLine("Author information for {0}", t);

    // Using reflection.
    System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t);  // Reflection.

    // Displaying output.
    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);
        }
      }
  }