Как перехватить вызов метода в C #? - PullRequest
144 голосов
/ 25 августа 2008

Для данного класса я хотел бы иметь функцию трассировки, т.е. я хотел бы регистрировать каждый вызов метода (сигнатура метода и фактические значения параметров) и каждый выход метода (только сигнатура метода).

Как мне это сделать, если:

  • Я не хочу использовать третьи лица AOP библиотеки для C #,
  • Я не хочу добавлять дублирующийся код ко всем методам, которые я хочу отследить,
  • Я не хочу изменять публичный API класса - пользователи класса должны иметь возможность вызывать все методы одинаково.

Чтобы сделать вопрос более конкретным, давайте предположим, что есть 3 класса:

 public class Caller 
 {
     public static void Call() 
     {
         Traced traced = new Traced();
         traced.Method1();
         traced.Method2(); 
     }
 }

 public class Traced 
 {
     public void Method1(String name, Int32 value) { }

     public void Method2(Object object) { }
 }

 public class Logger
 {
     public static void LogStart(MethodInfo method, Object[] parameterValues);

     public static void LogEnd(MethodInfo method);
 }

Как вызвать Logger.LogStart и Logger.LogEnd для каждого вызова Method1 и Method2 без изменения Caller.Call и без явного добавления вызовов в Traced.Method1 и Traced.Method2 ?

Редактировать: Каким было бы решение, если бы мне позволили немного изменить метод Call?

Ответы [ 15 ]

1 голос
/ 21 ноября 2009

вам нужно уговорить Айенде за ответ, как он это сделал: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx

1 голос
/ 18 сентября 2008

Вы можете использовать шаблон GOF Decorator и «декорировать» все классы, требующие трассировки.

Вероятно, это действительно практично только с контейнером IOC (но, как указывалось ранее, вы можете рассмотреть возможность перехвата метода, если собираетесь идти по пути IOC).

1 голос
/ 25 августа 2008

Я не знаю решения, но мой подход был бы следующим:

Украсьте класс (или его методы) пользовательским атрибутом. Где-то еще в программе, пусть функция инициализации отражает все типы, читает методы, украшенные атрибутами, и вводит некоторый код IL в метод. На самом деле может быть более практичным заменить метод заглушкой, которая вызывает LogStart, фактический метод, а затем LogEnd. Кроме того, я не знаю, можете ли вы изменить методы с помощью отражения, чтобы было удобнее заменить весь тип.

0 голосов
/ 12 июля 2015

Лучшее, что вы можете сделать до C # 6 с выпущенным именем nameof, это использовать медленные выражения StackTrace и linq.

например. для такого метода

    public void MyMethod(int age, string name)
    {
        log.DebugTrace(() => age, () => name);

        //do your stuff
    }

Такая строка может появиться в вашем лог-файле

Method 'MyMethod' parameters age: 20 name: Mike

Вот реализация:

    //TODO: replace with 'nameof' in C# 6
    public static void DebugTrace(this ILog log, params Expression<Func<object>>[] args)
    {
        #if DEBUG

        var method = (new StackTrace()).GetFrame(1).GetMethod();

        var parameters = new List<string>();

        foreach(var arg in args)
        {
            MemberExpression memberExpression = null;
            if (arg.Body is MemberExpression)
                memberExpression = (MemberExpression)arg.Body;

            if (arg.Body is UnaryExpression && ((UnaryExpression)arg.Body).Operand is MemberExpression)
                memberExpression = (MemberExpression)((UnaryExpression)arg.Body).Operand;

            parameters.Add(memberExpression == null ? "NA" : memberExpression.Member.Name + ": " + arg.Compile().DynamicInvoke().ToString());
        }

        log.Debug(string.Format("Method '{0}' parameters {1}", method.Name, string.Join(" ", parameters)));

        #endif
    }
0 голосов
/ 25 августа 2008
  1. Напишите свою собственную библиотеку АОП.
  2. Используйте отражение, чтобы сгенерировать прокси для ведения журналов над вашими экземплярами (не уверен, что вы можете сделать это без изменения какой-либо части существующего кода).
  3. Перепишите сборку и введите код регистрации (в основном такой же, как 1).
  4. Разместите CLR и добавьте протоколирование на этом уровне (я думаю, что это самое сложное решение для реализации, хотя я не уверен, что у вас есть необходимые хуки в CLR).
...