我可以从通用委托获取方法属性吗?

问题描述:

我确定论坛上已经有答案了,但到目前为止我还没有找到答案。按照this example我使用匿名方法连同代表以有不同的参数,但相同的返回类型作为一切工作的函数参数不同的方法:我可以从通用委托获取方法属性吗?

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

正如你看到的例子,我'真的很想能够从TestThat()函数中获取TestDescriptionAttribute属性。我已经通过Action参数包含了我的方法,但一直未能“找到”我的TargetHasOneTeam()方法。

+3

我认为这是唯一可能的,如果你使用一个表达式例如。 'System.Linq.Expressions.Expression '。然后,您可以将主体转换为MethodCallExpression,并使用里面的MethodInfo对象来查询属性... – MattDavey

+0

您的委托变成编译器生成的类,派生自类似于MulticastDelegate这样的包含您的方法信息的类。我认为有可能用反思来得到它。 –

+2

我已经在类似的情况下对这种模式进行了尝试。我发现它非常脆弱。仅供参考。 – usr

如果你改变TestThat(() => TargetHasOneTeam())(你包装一下你的委托到另一个动作)来TestThat(TargetHasOneTeam)和改变TestThat这样的:

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 
} 

会给你你所需要的。

随着表达式:

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

    var result = action.Compile()(); 
} 
+0

我明白如何做到这一点,如果TargetHasOneTeam ()没有参数,但是如果我还想用另一个函数做这个,比如说CheckTargetTeamName(string name)? – millejos

+2

然后将您的TestThat(...)更改为TestThat(表达式>)或TestThat(表达式>),并像@MattDavey所示的遍历表达式树。要调用真正的委托,您需要调用expression.Compile()并调用编译的lambda。 –

在这种特殊情况下,它本质上是无法访问的。您正在创建一个执行相关方法的lambda表达式。该lambda最终导致生成一个新方法,最终是Action委托人的参数。该方法与TargetHasOneTeam没有任何关系,只有在您深入了解正文中的IL指令时才显然。

您可以跳过lambda并在此处执行方法组转换。

TestThat(TargetHasOneTeam); 

现在TargetHasOneTeam被直接分配给委托实例,并会在Delegate::MethodInfo属性可见。

注:一般来说,虽然这是一个坏主意,正是你遇到的问题。方法上的属性不应该影响它满足委托实例化的能力。如果可能的话,我会避免这种类型的检查。

+0

瓦尔解释了如何做,但我想我同意你的看法,这似乎不是一个好主意,谢谢你对幕后发生的事情的解释。 – millejos

您可以通过Attribute.GetCustomAttribute获得任何成员的属性。首先检查属性是否定义。例如:

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

它将如何帮助获取从委托的* inside *中调用的方法的属性? –

+0

在我的示例中添加了一些细节;它没有被执行*从一个委托内* –

+1

我认为这个例子可以工作,如果我没有用匿名方​​法包装我的方法调用。(如果我使用下面的Val Bakhtin的例子,而不是我目前正在做的) – millejos

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