''.join('_'+c.lower() if c.isupper() else c for c in "DeathToCamelCase").strip('_')
re.sub("(.)([A-Z])", r'\1_\2', 'DeathToCamelCase').lower()
Вот что я придумал в качестве решения:
PostSharp или другое решение АОП было нецелесообразно в этой ситуации, поэтому, к сожалению, мне пришлось отказаться от этой идеи.
Похоже, что хотя имена и типы параметров можно задавать с помощью отражения, единственный способ получить доступ к значениям времени выполнения - это подключить отладчик.
См. Здесь для получения дополнительной информации:
microsoft.public.dotnet.framework
Так что это все еще оставило меня с проблемой ~ 50 методов, которые требовали добавления этого журнала вручную.
Отражение, чтобы спасти ...
public String GetMethodParameterArray()
{
var output = new StringBuilder();
output.AppendLine();
Type t = typeof(API);
foreach (var mi in t.GetMethods())
{
var argsLine = new StringBuilder();
bool isFirst = true;
argsLine.Append("object[] args = {");
var args = mi.GetParameters();
foreach (var pi in args)
{
if (isFirst)
{
isFirst = false;
}
else
{
argsLine.Append(", ");
}
argsLine.AppendFormat("{0}", pi.Name);
}
argsLine.AppendLine("};"); //close object[] initialiser
output.AppendLine(argsLine.ToString());
output.AppendFormat("Log(\"{0}\",args);", mi.Name);
output.AppendLine();
output.AppendLine();
}
return output.ToString();
}
Этот фрагмент кода перебирает методы класса и выводит массив object [], инициализированный аргументами, переданными в метод, и вызовом журнала, содержащим аргументы и имя метода.
Пример вывода:
object[] args = {username, password, name, startDate, endDate, cost};
Log("GetAwesomeData",args);
Затем этот блок можно вставить в начало метода для достижения необходимого эффекта.
Это больше ручной ввод, чем мне бы хотелось, но он намного лучше, чем ввод параметров вручную, и гораздо менее подвержен ошибкам.
Я не уверен, что API для доступа к стеку вызовов предоставляет средства для получения списка аргументов.
Однако существуют способы внедрить IL для перехвата вызовов методов и выполнения пользовательского кода.
Библиотека, которую я часто использую, - PostSharp , автор Gael Fraiteur, она включает приложение, которое запускает postbuild и внедряет IL в ваши выходные сборки в зависимости от аспектов, которые вы используете. Существуют атрибуты, с помощью которых вы можете украшать сборки, типы или отдельные методы. Например:
[Serializable]
public sealed class LoggingAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs eventArgs)
{
Console.WriteLine("Entering {0} {1} {2}",
eventArgs.Method.ReflectedType.Name,
eventArgs.Method,
string.Join(", ", eventArgs.Arguments.ToArray()));
eventArgs.MethodExecutionTag = DateTime.Now.Ticks;
}
public override void OnExit(MethodExecutionArgs eventArgs)
{
long elapsedTicks = DateTime.Now.Ticks - (long) eventArgs.MethodExecutionTag;
TimeSpan ts = TimeSpan.FromTicks(elapsedTicks);
Console.WriteLine("Leaving {0} {1} after {2}ms",
eventArgs.Method.ReflectedType.Name,
eventArgs.Method,
ts.TotalMilliseconds);
}
}
После этого вы можете просто украсить метод, который вы хотите с этим атрибутом:
[Logging]
public void SomeMethod(String p1, String p2, int p3)
{
//..
}
Хорошо params помогают с вызовом журнала, но не помогают существующим сигнатурам методов. Ведение журнала с использованием AOP Framework может быть более продуктивным подходом?
Существует некоторая функциональность с динамической системой типов, которая может это делать, но тогда ваш класс должен наследовать от динамических базовых классов
Если вы используете Postsharp, вы можете просто добавить атрибут к методу, который вы хотите логировать. Внутри этого атрибута вы можете написать код протоколирования, а также предоставить необходимые аргументы. Это известно как сквозные проблемы и AOP (Aspect oriented programming)
.может не работать в некоторых сценариях, но должно помочь вам начать :)
class Program
{
static void Main(string[] args)
{
M1("test");
M2("test", "test2");
M3("test", "test2", 1);
Console.ReadKey();
}
static void M1(string p1)
{
Log(MethodBase.GetCurrentMethod());
}
static void M2(string p1, string p2)
{
Log(MethodBase.GetCurrentMethod());
}
static void M3(string p1, string p2, int p3)
{
Log(MethodBase.GetCurrentMethod());
}
static void Log(MethodBase method)
{
Console.WriteLine("Method: {0}", method.Name);
foreach (ParameterInfo param in method.GetParameters())
{
Console.WriteLine("ParameterName: {0}, ParameterType: {1}", param.Name, param.ParameterType.Name);
}
}
}
Что ж, если вы просто хотите передать значения, вы можете обмануть и определить массив объектов:
public static void LogParameters(params object[] vals)
{
}
Это приведет к блокировке типов значений, а также не даст вам никаких имен параметров, однако .
Допустим, у меня есть метод:
public void SomeMethod(String p1, String p2, int p3)
{
#if DEBUG
LogParamaters(p1, p2, p3);
#endif
// Do Normal stuff in the method
}
Обновление: к сожалению, отражение не сделает все автоматически за вас. Вам нужно будет указать значения, но вы можете использовать отражение, чтобы указать имена / типы параметров:
Как вы можете получить имена параметров метода?
Таким образом, подпись метода изменится на что-то вроде:
public static void LogParameters(string[] methodNames, params object[] vals)
{ }
Затем вы можете принудительно / предположить, что каждый индекс в каждой коллекции совпадает, так что methodNames [0]
имеет значение vals [0]
.
Пока вы знаете, какие типы ожидать, вы можете регистрировать их в базе данных SQL. Напишите метод, который выполняет проверку типа, а затем заполняет соответствующий столбец БД значением параметра (аргумента). Если у вас есть пользовательский тип, вы можете использовать имя типа и сохранить его как строку в отдельном специальном столбце.
-Edit
Также, используя метод расширения MethodBase.Name
, вы можете связать ваши параметры с методом, который принял их в качестве аргументов, как упоминалось в другом сообщении ниже. Это было бы удобным способом отслеживания всех использованных методов, с какими аргументами и какого типа.
Это хотя бы смутно хорошая идея? :)
.