如何枚举传递的方法参数

时间:2010-01-14 08:08:31

标签: c# reflection

可以枚举被调用的方法参数类型/信息,如下所示:

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

因此有一个通用的方法来收集所有参数及其值。

3 个答案:

答案 0 :(得分:9)

<强>更新

通过尝试解释所有内容,我看起来“过于复杂”了最初的答案。以下是答案的简短版本。

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;
        }
    }

}

请注意,如果使用表达式树,则代码很大程度上取决于传递给方法的表达式。我展示了一个使用常量,一个使用变量。但当然可以有更多场景。您可以重构此代码以在两种情况下使用单个方法,但我认为这样可以更好地说明问题。

答案 1 :(得分:1)

好的,这就是交易。

您不能这样做,不能使用托管语言。我看不出有人会让你控制堆栈帧。在某种程度上,这就是你想要的。因为您需要获取信息的信息。

现在运行时知道这一点,它拥有所有信息,但你无法对如何创建堆栈框架做出假设,因为你不打算这样做。

因此,只有一种方法可以解决这个问题。分析API。

我结束here。在分析API的功能内。我敢打赌有一种方法可以让你通过调用托管代码中的非托管类来深入了解参数值。

现在,我不会这样做,因为已经有很好的分析工具,JetBrains dotTrace来命名一个和VS2010中的IntelliTrace所有这些令人头痛的东西都会消失...... IntelliTrace会让你花时间去调试。< / p>

另一种显而易见的方法是完全满足于此,但最终可能会尝试使用它,它总是以这种方式完成,但我一生都不会将此代码放在生产环境中。

// compile with unsafe
unsafe
{
    var p = stackalloc int[1];
    var baseAddr = p - sizeof(int);
}

现在,你不能写信给baseAddr但你应该被允许阅读它。棘手的部分是理解堆栈帧,并且必须符合调用约定,并且必须确定一定的时间。 Here's对那些东西进行了解决,这是快速调用。

使用此信息和ParameterInfo对象,您应该能够完成参数。

由于你将使用原始指针,你需要将它们变成托管对象,并且有class

你去,疯了!

一个很大的警告,当你走上堆栈时你会发现什么,不会是你期望的。因为参数可以放在寄存器中,所以无法从托管代码中访问寄存器。

答案 2 :(得分:1)

您可以使用MethodInfo.GetCurrentMethod().GetParameters()获取方法参数列表。但是通过反思来获取他们的价值是不可能的。