[英]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.