[英]How to enumerate passed method parameters
可以像這樣枚舉被調用的方法參數類型/信息:
private void SomeMethod(int thisValue, string thatValue)
{
StackTrace stackTrace = new StackTrace();
foreach (ParameterInfo pInfo in stackTrace.GetFrame(0).GetMethod().GetParameters())
{
string name = pInfo.Name;
string type = pInfo.GetType().ToString();
}
}
但是有沒有什么辦法可以得到每個參數的實際對象呢?
編輯:我的目標是枚舉所有參數並獲取它們的值。 使用 LinQ 表達式,可以像這樣獲得參數值:
private void SomeMethod(int thisValue, string thatValue)
{
object valueOfThis = GetParameterValue(() => thisValue);
object valueOfThat = GetParameterValue(() => thatValue);
}
private object GetParameterValue<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
return ((FieldInfo)body.Member).GetValue(((ConstantExpression)body.Expression).Value);
}
但我想做的是:
foreach (fooObject o in thisMethod.GetParameterObjects())
{
object someValue = GetParameterValue(() => fooObject);
}
從而有一個用於收集所有參數及其值的通用方法。
更新:
看起來我試圖解釋一切,使最初的答案“過於復雜”。 這是答案的簡短版本。
private static void SomeMethod(int thisValue, string thatValue)
{
IEnumerable<object> parameters = GetParameters(() => SomeMethod(thisValue, thatValue));
foreach (var p in parameters)
Console.WriteLine(p);
}
private static IEnumerable<object> GetParameters(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (MemberExpression a in body.Arguments)
{
var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value);
yield return test;
}
}
這是帶有一些解釋的長版本。
事實上,如果使用表達式樹,則不需要在方法內部枚舉其參數。
static void Main(string[] args)
{
// First approach.
IEnumerable<object> parameters = GetParametersFromConstants(() => SomeMethod(0, "zero"));
foreach (var p in parameters)
Console.WriteLine(p);
// Second approach.
int thisValue = 0;
string thatValue = "zero";
IEnumerable<object> parameters2 = GetParametersFromVariables(() => SomeMethod(thisValue, thatValue));
foreach (var p in parameters2)
Console.WriteLine(p);
Console.ReadLine();
}
private static void SomeMethod(int thisValue, string thatValue)
{
Console.WriteLine(thisValue + " " + thatValue);
}
private static IEnumerable<object> GetParametersFromVariables(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (MemberExpression a in body.Arguments)
{
var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value);
yield return test;
}
}
private static IEnumerable<object> GetParametersFromConstants(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (ConstantExpression a in body.Arguments)
{
var test = a.Value;
yield return test;
}
}
}
請注意,如果您使用表達式樹,您的代碼在很大程度上取決於傳遞給方法的表達式。 我已經展示了一種使用常量和一種使用變量。 但當然可以有更多的場景。 您可以重構此代碼以在兩種情況下使用單一方法,但我認為這樣可以更好地說明問題。
好的,這就是交易。
你不能這樣做,不是從托管語言。 我看不出任何人會允許您控制堆棧幀。 在某種程度上,這就是您想要的。 因為您需要信息來獲取值。
現在運行時知道這一點,它擁有所有信息,但是您不能假設它將如何創建堆棧幀,因為您不打算這樣做。
因此,只有一種方法可以解決這個問題。 分析 API。
我到此為止。 在分析 API 的功能中。 我敢打賭,有一種方法可以讓您通過從托管代碼調用非托管類來深入了解參數值。
現在,我不會這樣做,因為已經有很棒的分析工具了,JetBrains dotTrace 就是其中之一,而且有了 VS2010 中的 IntelliTrace,所有這些令人頭疼的問題都會消失……IntelliTrace 將讓您進行時間旅行調試。
另一種明顯的方法是完全 foobar,但最終可能會很有趣,它總是可以通過這種方式完成,但我一生中永遠不會將此代碼放在生產環境中。
// compile with unsafe
unsafe
{
var p = stackalloc int[1];
var baseAddr = p - sizeof(int);
}
現在,您不能寫入baseAddr
但應該允許您讀取它。 棘手的部分是理解堆棧幀,這與調用約定有關,並且您必須提前了解。 這是這些東西的簡要介紹,它是快速呼叫。
有了這些信息和 ParameterInfo 對象,您應該能夠逐步了解參數。
由於您將使用原始指針,因此您需要將它們轉換為托管對象,並且有一個用於此的類。
給你,發瘋!
但是有一個很大的警告,當你走上堆棧時,你會發現什么,不會是你所期望的。 因為參數可以放在寄存器中,並且不能從托管代碼中訪問寄存器。
您可以使用MethodInfo.GetCurrentMethod().GetParameters()
獲取方法參數列表。 但是通過反思來獲得它們的價值是不可能的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.