2011-11-22 46 views
22

我知道這個網站上有幾個答案,我很抱歉,如果這是以任何方式重複,但我發現的所有的都不做我想要的做。使用表達式獲取方法的名稱

我想指定方法信息,所以我可以通過不使用字符串以類型安全的方式獲取名稱。 所以我試圖用表達式來提取它。

說,我想在此界面獲得方法的名字:

MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression) 
{ 
     return ((MethodCallExpression)expression.Body).Method; 
} 

我可以調用輔助方法如下:

public interface IMyInteface 
{ 
    void DoSomething(string param1, string param2); 
} 

目前我可以用這種方法獲取名稱:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null)); 
Console.WriteLine(methodInfo.Name); 

但是我正在尋找版本,我可以得到的方法名稱沒有指定參數(NULL,NULL)

這樣的:

var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething); 

但所有嘗試都失敗編譯

有沒有辦法做到這一點?

+0

目前沒有VS,但我認爲你應該嘗試在非泛型替代方法中接受'Delegate'或'Action '。在這種情況下,您應該可以傳遞方法組 – Snowbear

+0

如果將輸入更改爲委託,它將不會編譯,並且錯誤無法將方法組轉換爲委託。如果我將其更改爲Action <字符串,字符串>它似乎工作,但表達式轉換爲UnaryExpression和該方法爲空,我不能看到從UnaryExpression – Andre

+0

affer轉換得到此。你嘗試過'var methodInfo = GetMethodInfo (DoSomething);'而不是'var methodInfo = GetMethodInfo (x => x.DoSomething);'?此外,我仍然認爲指定(默認(字符串),默認(字符串)) - 更具可讀性,並且您可以在這種情況下支持重載方法 –

回答

11
x => x.DoSomething 

爲了使這個編譯我看只有兩種方式:

  1. 轉到非通用的方式,並指定它的參數爲Action<string, string>
  2. 指定Action<string, string>爲您目標委託類型由你自己:GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))

,如果你都OK去與第二個,它可以讓你忽略的參數,然後如下,你可以寫你的GetMethodInfo方法:

MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression) 
    { 
     var unaryExpression = (UnaryExpression) expression.Body; 
     var methodCallExpression = (MethodCallExpression) unaryExpression.Operand; 
     var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last(); 
     var methodInfo = (MemberInfo) methodInfoExpression.Value; 
     return methodInfo; 
    } 

它適用於您的界面,但可能需要進行一些泛化才能使用任何方法進行工作,這取決於您。

+0

我希望有一點不詳細,但不過,這工作得很好,謝謝。 – Andre

+5

我很好奇,我知道它已經是2年了,所以我希望你仍然可以回答:有沒有一種方法來允許這樣的語法:'GetMethodInfo (x => x.DoSomething);'? – 9ee1

+2

關於這種方法的說明,這僅適用於.NET 3.5和4.0,不適用於4.5。在後一種情況下,方法調用表達式的表示方式不同,因此參數順序不同。在這種情況下,你必須這樣做:var methodInfoExpression =(ConstantExpression)methodCallExpression.Object;'。總的來說,我覺得這種方法太麻煩了。 – nawfal

1

如果您的應用程序將允許在Moq的依賴(或者類似的庫),你可以做這樣的事情:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething)); 
     Console.WriteLine(methodName); 
    } 

    static string GetMethodName<T>(Func<T, Delegate> func) where T : class 
    { 
     // http://code.google.com/p/moq/ 
     var moq = new Mock<T>(); 
     var del = func.Invoke(moq.Object); 
     return del.Method.Name; 
    } 
} 

public interface IMyInteface 
{ 
    void DoSomething(string param1, string param2); 
} 
+1

-1,這會給出句柄到你正在創建的中間匿名委託(傳遞給'GetMethodInfo'方法),而不是你想要表示的實際成員('DoSomething')。 – nawfal

+0

@nawful原始海報請求字符串Name,而不是原始的MethodInfo。我已經更新了我的示例來澄清。 – Wally

+0

我擔心,如果它仍然沒有給出正確的結果。你測試過了嗎? – nawfal

4

問題在於x.DoSomething代表一個方法組。而且你必須以某種方式明確指定要將該方法組轉換爲哪種委託類型,以便可以選擇該組的正確成員。如果該組只包含一個成員,則無關緊要。

編譯器可能是推斷你是指那一個,但它不這樣做。 (我認爲這樣可以讓你的代碼不會中斷,如果你添加另一個超載的方法。)

Snowbear的回答包含很好的建議,可能的解決方案。

+0

+1提到實際問題.. – nawfal

2

這是對舊問題的新答案,但是迴應了接受答案的「詳細」投訴。它需要更多的代碼,但結果是一個語法像:

MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething); 

,或者用一個返回值

MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn); 

其中

object DoSomethingWithReturn(string param1, string param2); 

就像框架提供行動<方法>和Func <>代表最多16個參數,您必須具有GetActionInfo和GetFuncInfo方法,可以接受多達16個參數(或更多,儘管我認爲重構如果你有16個參數的方法是明智的)。更多的代碼,但語法上的改進。

+1

我希望沒有詳細的意思,我可以這樣做:'GetMethodInfo (x => x.DoSomething);'?我有點困惑如何實施你的建議。你能提供一個快速的代碼示例嗎? – 9ee1

+0

在不傳遞參數的情況下完成它的唯一方法是將參數類型包含在通用規範中,即GetMethodInfo (x => x.DoSomething)。 –

11

以下是.NET 4.5兼容:

public static string MethodName(LambdaExpression expression) 
{ 
    var unaryExpression = (UnaryExpression)expression.Body; 
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 
    var methodCallObject = (ConstantExpression)methodCallExpression.Object; 
    var methodInfo = (MethodInfo)methodCallObject.Value; 

    return methodInfo.Name; 
} 

你可以像x => x.DoSomething表達式中使用它,但它需要一些包裝成不同類型的方法泛型方法。

這裏是一個向後兼容的版本:

private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null; 

public static string MethodName(LambdaExpression expression) 
{ 
    var unaryExpression = (UnaryExpression)expression.Body; 
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 
    if (IsNET45) 
    { 
     var methodCallObject = (ConstantExpression)methodCallExpression.Object; 
     var methodInfo = (MethodInfo)methodCallObject.Value; 
     return methodInfo.Name; 
    } 
    else 
    { 
     var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last(); 
     var methodInfo = (MemberInfo)methodInfoExpression.Value; 
     return methodInfo.Name; 
    } 
} 

檢查this sample code on Ideone。請注意,Ideone沒有.NET 4.5。

相關問題