Прежде всего вы не можете запросить время выполнения для этой информации магически, неявно.
Это правда, что вы можете неявно узнать полную идентификацию текущего исполняемого метода, его вызывающего абонента, вызывающего абонента и всех элементов stacktrace (за исключением конкретных общих аргументов в случае общих методов) путем создания экземпляра класса StackTrace
или путем вызова метода MethodBase.GetCurrentMethod()
.
Также верно, что в полученных экземплярах MethodBase
содержится информация о параметрах методов, и поэтому вы можете узнать имена параметров, но это то, где все закончится. Если бы механизм, позволяющий вам неявно исследовать значения параметров или локальных переменных, был бы изобретен, тогда все было бы намного медленнее.
Что вы, можете сделать, немного помочь себе, с помощью класса Expression<Lambda>
и его окружения. Это будет немного медленнее, но вы можете выбрать, хотите ли вы просматривать и легко управлять кодом, или очень сложно управлять, и очень быстрый код.
Expression<Lambda>
как LINQ удается не сделать полное сканирование таблицы таблиц базы данных, а понять, что вы сделали с вашим запросом, и перевести это (во время выполнения) в SQL или любой другой язык, который вы можете себе представить (в зависимости на фактическом провайдере LINQ).
Прежде всего, я хотел бы предложить проблемы расщепления на 2 категории:
- Получение имен и значений (как неявно, как это возможно)
- Использование имен и значений (там, где вы хотите)
Чтобы это произошло, вам нужно подумать о сущности, которая может удерживать результаты пункта 1. В моем предположении, что это будет своего рода Dictionary<string, object>
, но вы можете делать все, что вам подходит.
Мое предложение может быть использовано следующим образом:
public void SomeMethod(string x, int y) {
IDictionary<string, object> paramValues = Helper.TapInto(
() => x,
() => y
);
// paramValues["x"] an paramValues["y"] will hold the values of x and y
}
Так, на биту кодирования. Вы можете написать Helper
класс следующим образом:
public static class Helper {
}
В этом Helper
классе вы могли бы придумать статический метод (я назвал мое TapInto
, возможно, это не лучшее название для него), который принимает примитивный массив Expression<Func<object>>
экземпляров. Он делает это с модификатором params
, так что вы можете легко передать им неявные лямбды. В качестве возврата он дает хеш-таблицу от string
до object
, представляющую имена «декомпилированных» переменных и связанные с ними значения.
В моем случае я также создал приватную перегрузку того же метода, который на самом деле является методом «расширения», чтобы сделать код более понятным.
public static class Helper {
// ... an overload of the TapInto method is about to appear right here
public static IDictionary<string, object> TapInto(params Expression<Func<object>>[] parameterTouchers) {
var result = new Dictionary<string, object>();
foreach (var toucher in parameterTouchers) {
string name;
object value;
toucher.TapInto(out name, out value);
result[name] = value;
}
return result;
}
Таким образом, весь общедоступный метод выполняет перебор по списку и накапливает полученные результаты в словаре.
Теперь давайте посмотрим на настоящее волшебство, которое происходит в toucher.TapInto(out name, out value)
вызова:
public static class Helper {
private static void TapInto(this Expression<Func<object>> @this, out string name, out object value) {
Expression expression = @this.Body;
if (expression is UnaryExpression)
expression = (expression as UnaryExpression).Operand;
name = (expression as MemberExpression).Member.Name;
Func<object> compiledLambda = @this.Compile();
value = compiledLambda();
}
// ... public helper method right here
}
Что мы делаем здесь «мы ищем внутри» лямбда с увеличительным стеклом. Поскольку мы собираемся использовать материал, кроме object
переменных это неизбежно наблюдать неявное преобразование типа
.. int someParameter ..
object obj = someParameter;
, которая является неявной только в реальном коде C#, но на самом деле скомпилирован как явного преобразования:
object obj = (object)someParameter;
Но у вас может быть простой параметр object
, например object anotherParam
, и в этом случае конверсии вообще не будет.
Вот почему, наблюдая за сложными деталями выражения, я предполагаю, что могу найти конверсию (представленную классом UnaryExpression
) или нет.
На самом деле это, как говорят: В данном конкретном случае, мой контракт на код вызова является то, что он может послать мне только материал, который попадает в эти 2 категории:
- Немедленное
object
переменная ссылка: () => someObjectVariable
- Variable ссылки с конверсией:
() => (object)x
в контракте также случайно говорится, что «преобразование» бит могут быть заменены на UnaryExpression
, например: () => !someBool
.
Он также утверждает, что вы не можете сделать что-то вроде:
() => 123
- или
() => a + b + c + 100
- или что-нибудь еще в тех направлениях
Таким образом, чтобы обернуть его:
- Yo вы могли бы написать свой симпатичный помощник
- Вы можете использовать его везде, где хотите его использовать, для создания карт между именами параметров и их значениями, хотя это не 100% неявное, , но, по крайней мере, оно не будет компилироваться, если вы переименуете параметр без полного рефакторинга или он позволит вам переименовать ссылки параметров, если вы решите переименовать параметры с помощью рефакторинга (он также работает с полями, локальными переменными и т. д.)
- Передайте словари между интересующими вас частями вашего кода в них и используйте их соответственно!
Да, это способ сделать это. Я надеялся достичь своей цели, не обращаясь к параметрам явно, но время также является параметром :) – OKB