Как перечислить пройденные параметры метода
Можно перечислять вызываемые типы/данные параметров метода следующим образом:
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);
}
И тем самым иметь общий метод для сбора всех параметров и их значений.
Ответы
Ответ 1
UPDATE:
Похоже, я "перекомпилировал" первоначальный ответ, пытаясь объяснить все. Вот короткая версия ответа.
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;
}
}
}
Обратите внимание, что если вы используете деревья выражений, ваш код сильно зависит от выражения, переданного методу. Я показал один с использованием констант и один с использованием переменных. Но, конечно, может быть больше сценариев. Вы можете реорганизовать этот код на использование одного метода для обоих случаев, но я решил, что это лучше иллюстрирует проблему.
Ответ 2
Хорошо, так вот сделка.
Вы не можете этого сделать, а не с управляемого языка. Я не вижу, как кто-то позволит вам взять под контроль стек стека. И таким образом, что вы хотите. Потому что вам нужна информация для получения значений.
Теперь время выполнения знает об этом, оно содержит всю информацию, но вы не можете делать предположения о том, как это будет происходить при создании фрейма стека, потому что вы не предназначены для этого.
Эрго, есть только один способ сделать это. API профилирования.
В конечном итоге здесь. В рамках функций API профилирования. Уверен, что есть способ сделать это, чтобы позволить вам вставлять значения параметров, вызывая неуправляемый класс из управляемого кода.
Теперь я бы этого не делал, потому что там уже были большие инструменты для профилирования, JetBrains dotTrace, чтобы назвать одно, а с IntelliTrace в VS2010 все эти головные боли просто исчезнут... IntelliTrace позволит вам выполнять временную отладку.
Другой и очевидный способ сделать это полностью foobar, но может закончиться весело провести эксперимент, он всегда может быть сделан таким образом, но я никогда в жизни не ставил этот код в производственную среду.
// compile with unsafe
unsafe
{
var p = stackalloc int[1];
var baseAddr = p - sizeof(int);
}
Теперь вы не можете писать в baseAddr
, но вам должно быть разрешено его прочитать. Сложная часть состоит в том, чтобы осмыслить фреймы стека и иметь дело с вызывающим соглашением и что вы должны знать наверняка главу времени. Вот прогон этого материала и fastcall.
С помощью этой информации и объектов ParameterInfo вы сможете пройти через аргументы.
Поскольку вы будете работать с необработанными указателями, вам нужно будет превратить их в управляемые объекты, и для этого вам будет класс.
Там ты идешь, иди орехи!
Большое предупреждение, хотя, то, что вы найдете при подходе к стеку, не будет тем, что вы ожидаете. Поскольку аргументы могут быть помещены в регистры, и регистры не могут быть доступны из управляемого кода.
Ответ 3
Вы можете использовать MethodInfo.GetCurrentMethod().GetParameters()
, чтобы получить список параметров метода. Но невозможно получить их значения путем отражения.