Как я могу получить значения параметров вызывающего метода?

Вопрос

Я пишу код, который должен иметь возможность получать значения параметров из метода, вызываемого в class. Я знаю, как добраться до массива ParameterInfo [], но я не знаю, как получить значения. Возможно ли это?

Если это так, я думаю, что это имеет какое-то отношение к использованию свойства MethodBody объекта MethodInfo, что позволяет вам проверять stream IL, включая свойства, но я не знаю, как это сделать, и я не нашел применимый код в Google.

Код

// Finds calling method from class that called into this one public class SomeClass { public static void FindMethod() { for (int i = 1; i < frameCount; i++) { var frame = new StackFrame(i); var methodInfo = frame.GetMethod(); if (methodInfo.DeclaringType != this.GetType()) { string methodName = frame.GetMethod().Name; var paramInfos = methodInfo.GetParameters(); // Now what?? How do I get the values from the paramInfos break; } else if (i == frameCount - 1) { throw new TransportException("Couldn't find method name"); } } } } 

Вы не можете этого сделать, не задумываясь над самим стекем (и это хрупко, поскольку многие оптимизации могут означать, что кадр стека не является тем, что вы ожидаете, или даже тот, что переданный параметр не является тем, что предложит подпись метода (это вполне возможно для оптимизирующий компилятор JIT, чтобы определить, что вы используете только вспомогательное поле объекта / структуры и передаете это вместо этого).

ПараметрInfo просто сообщает вам подпись метода как скомпилированную, а не переданные значения.

Единственный реалистичный способ добиться этого – через инъекцию кода (через что-то вроде АОП) для создания данных и делать то, что вы хотите с ним, на основе анализа ИЛ.

Это, как правило, не очень хорошая идея, если вам нужно отлаживать что-то, используя отладчик, если вам нужно записывать что-то в явном виде о том, что вы регистрируете.

Чтобы быть ясными, простые рефлективные методы не могут достичь того, чего вы желаете с полной общностью

Джонатан Келджо в Microsoft говорит, что в этой группе новостей :

К сожалению, единственный простой способ получить информацию о аргументах из callstack сегодня – это отладчик. Если вы пытаетесь сделать это как часть регистрации ошибок в приложении, и вы планируете отправить журнал ошибок в свой отдел поддержки, мы надеемся, что в будущем вы будете использовать мини-компьютеры для этой цели. (Сегодня использование minidump с управляемым кодом немного проблематично, поскольку оно не содержит достаточной информации, чтобы даже получить трассировку стека по умолчанию. Minidump с кучей лучше, но не так «мини», если вы знаете, что я имею в виду.)

Пурист сказал бы, что позволяя людям писать код, который может получить аргументы от функций в другом месте в callstack, побудил бы их разорвать инкапсуляцию и создать очень хрупкий код перед лицом изменений. (У вашего сценария нет этой конкретной проблемы, но я слышал и другие запросы для этой функции. Во всяком случае большинство этих запросов могут быть решены другими способами, например, с использованием хранилищ streamов.) Однако, что более важно, были бы последствия для безопасности из этого – приложения, которые допускают плагины, будут подвержены риску того, что эти плагины соскабливают стек для получения конфиденциальной информации. Мы могли бы отметить функцию как требующую полного доверия, но это сделало бы ее непригодной для почти всех сценариев, о которых я слышал.

Джонатан

Итак … Я думаю, короткий ответ: «Я не могу». Это отстой.

Да, ты можешь это сделать.

Что вам нужно сделать, это использовать дизассемблер IL (который можно реализовать в пространстве имен System.Reflection.Emit), чтобы найти Операнд, который содержит значение параметра, которое вы ищете.

Начните с этого вопроса SO: reflection C # и поиск всех ссылок

Затем используйте class, упомянутый в ответах (из Mono.Reflection), чтобы выполнить ваш осмотр. Что-то вроде этого:

  var instructions = method.GetInstructions(); foreach (var instruction in instructions) { var methodInfo = instruction.Operand as MethodInfo; if(methodInfo == null) { continue; } if (instruction.OpCode.Name.Equals("call") && methodInfo.Name.Equals("YourMethodHere")) { var value = (CastToMyType)instruction.Previous.Operand; // Now you have the value... } } 

Вы не можете сделать это с помощью StackFrame или StackTrace. Тем не менее, вы можете использовать некоторую инфраструктуру перехвата (такую ​​как материал AOP из Spring.NET), чтобы вы могли получить значения параметров.

Глянь сюда:

Можете ли вы получить список переменных в стеке на C #?

Я не думаю, что это возможно, основываясь на всех комментариях к моему ответу. Класс PropertyInfo имеет метод GetValue, но для этого требуется, чтобы у вас был фактический объект, из которого вы хотите получить значение.

Давайте будем гением компьютера.