tags:

views:

130

answers:

3

One can enumerate the called method parameter types/information like this:

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

But is there any way to get the actual object of each parameter?

EDIT: My goal is to enumerate all parameters and get their values. Using LinQ Expressions, one can get the parameter value like so:

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

But what I would like to do is something like:

foreach (fooObject o in thisMethod.GetParameterObjects())
{
  object someValue = GetParameterValue(() => fooObject);
}

And thereby have a generic method for collection all parameters and their values.

+1  A: 

Okay, so here's the deal.

You can not do that, not from a managed language. I don't see how anyone would allow you to take control of the stack frame. And in a way that's what you want. Because you need the information to get the values.

Now the run-time knows this, it has all the information, but you can not make assumptions on how it will go about creating a stack frame, because you are not meant to do this.

Ergo, there's only one way to go about this. The profiling API.

I end up here. Within the functions of the profiling API. I bet there's a way to do this that let's you dig into the parameter values by invoking a unmanaged class from managed code.

Now, I wouldn't do this because there's great profiling tools out there already, JetBrains dotTrace to name one and with IntelliTrace in VS2010 all these headaches will simply go away... IntelliTrace will let you do time traveling debugging.

The other and obvious way to do this is totally foobar, but might end up fun to experiment with, it can always be done this way, but I would never in my life put this code in a production environment.

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

Now, you can not write to baseAddr but you should be allowed to read it. The tricky part is to make sense of the stack frames and that has to with the calling convention and that you must know for certain a head of time. Here's a run down of that stuff and it's fastcall.

With this information and the ParameterInfo objects you should be able to walk your way through the arguments.

Since you'll be working with raw pointers you'll need to make those into managed objects, and there's a class for that.

There you go, go nuts!

A big warning though, what you'll find as you walk up the stack, won't be what you expect. Because arguments can be placed in registers and registers can not be accessed from within managed code.

John Leidegren
A: 

You can use MethodInfo.GetCurrentMethod().GetParameters() to get a list of method parameters. But it's impossible to get their values by reflection.

Mehdi Golchin
@Mehdi. Yes, the GetParameters() method was already covered in my question, thanks.
Magnus Johansson
A: 

UPDATE:

Looks like I "overcomplicated" the initial answer by trying to explain everything. Here is the short version of the answer.

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

And here is the long version with some explanations.

In fact, if you use expression trees, you don't need to be inside a method to enumerate its parameters.

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

}

Note, that if you use expression trees, your code depends a lot on an expression passed to the method. I have shown one using constants and one using variables. But of course there can be more scenarios. You can refactor this code to use a single method for both cases, but I decided that is illustrates the problem better this way.

Alexandra Rusina
Thanks for the answer. However, I was looking for something that could be put inside SomeMethod() to make it generic. The example you provided forces one to call the custom code before SomeMethod().
Magnus Johansson
No, you don't need to call anything before SomeMethod. Take the code marked as "second approach" (just skip the variables declaration and initialization) and paste it to the the method. You will get the parameters enumeration inside of the method. I just wanted to point out that you can do it from anywhere, not necessarily from the method body.
Alexandra Rusina