How to enumerate passed method parameters
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.
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.