Получить MethodInfo метода, который находится перед активным в стеке вызовов? - PullRequest
3 голосов
/ 30 ноября 2011

Я создал оболочку для библиотеки журналов, внутри которой я хочу регистрировать параметры внешнего метода, выдавшего исключение:

public void MethodA(string param1, int param2)
{
  try
  {
    throw new Exception();
  }
  catch(Exception ex)
  {
    Logger.Error(ex);
  }
}

...

public static void Error(Exception ex)
{
...
}

Как видите, я хотел бы получить информацию о методе MethodA на уровне метода Error. Я был бы более чем счастлив просто использовать:

ParameterInfo[] pi = new StackFrame(1).GetMethod().GetParameters();

потому что у него есть все, но я только что прочитал несколько SO сообщений о плохой производительности. Мне кажется, что, может быть, тогда это не самый лучший способ (с другой стороны, мы говорим об обработке исключений - на данный момент это уже не так уж важно). Тем не менее, я не могу найти традиционный подход, используя Reflection для решения этой проблемы. Возможно, кто-то может просветить меня?

Или, может быть, вы все равно выбрали бы решение StackFrame? Это приложение asp.net с довольно небольшими требованиями к ультра производительности.

Edit1: Может быть, я не был уверен, что я хочу написать. Я использую решение StackFrame, даже если получение кадра может занять до 20 мс, но мне действительно любопытно, как получить похожий результат с отражением.

Ответы [ 4 ]

1 голос
/ 01 декабря 2011

Или, может быть, вы все равно выбрали бы решение StackFrame?Это приложение asp.net с довольно небольшими требованиями к ультра производительности.

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

Наша группа пишет программное обеспечение для торговли, которое мы тщательно отслеживаем на предмет производительности.Пока сгенерированные исключения не встречаются на критическом пути, у вас все в порядке.Обычно, если выдается необработанное исключение, производительность не имеет значения: пользователь не ждет ответа.Или этот ответ не будет получен из-за исключения.

Обратите внимание, что вы можете выполнить проверку индекса, прежде чем использовать жестко запрограммированный новый StackFrame (1).

1 голос
/ 01 декабря 2011

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

using Collections = MikeNakis.Abstract.Collections;
using Diagnostics = System.Diagnostics;
using Generic = System.Collections.Generic;
using Reflection = System.Reflection;
[...]
    ///<summary>Collect stack frames</summary>
    ///<param name="frames_to_skip">Number of frames to skip (usually 1)</param>
    ///<param name="type">When invoking from within a constructor, specify the type of the containing class so as to
    ///skip any other constructors.</param>
    public static Generic.IList<Diagnostics.StackFrame> CollectStackframes( int frames_to_skip, System.Type type = null )
    {
        var frames = new Diagnostics.StackTrace( frames_to_skip + 1, fNeedFileInfo:true ).GetFrames();
        int i = 0;
        if( type != null )
        {
            for( ;  i < frames.Length;  i++ )
            {
                Reflection.MethodBase method = frames[i].GetMethod();
                if( !method.IsConstructor )
                    break;
                //Does not work: if( method.DeclaringType == type )
                //Does not work: if( Equals( method.DeclaringType, type ) )
                //Does not work: if( Equals( method.DeclaringType.TypeHandle, type.TypeHandle ) )
                if( Equals( method.DeclaringType.GUID, type.GUID ) )
                {
                    i++;
                    break;
                }
            }
        }
        var list_of_frame = new Generic.List<Diagnostics.StackFrame>( frames.Length - i );
        for( ;  i < frames.Length;  i++ )
            list_of_frame.Add( frames[i] );
        return Collections.Util.NewListReadonly( list_of_frame );
    }

(Примечание: Collections.Util.NewListReadonly() - мой метод статической утилиты, который создает список только для чтения и возвращает его интерфейс IList<T>.)

Я не знаю о производительности new Diagnostics.StackTrace(): я бы предположил, что он такой же медленный, как использование отражения (вероятно, он считается отражением), но, вероятно, немного быстрее, чем выбрасывает исключение.

Я вас разочарую по поводу части параметров: я не нашел способа получить содержимое параметров для методов во время выполнения. Таким образом, все, что вы можете надеяться получить, это типы и имена параметров, но не их значения. Если вы найдете способ, пожалуйста, дайте мне знать.

1 голос
/ 01 декабря 2011

У этого вопроса есть несколько различных аспектов:

Я хочу записать параметры внешнего метода

Вы можете использовать метод перехвата, основанный наВаш любимый контейнер IoC, но я честно нашел, что лучший подход - это просто передать параметры в информацию об исключении:

throw new Exception("Something went wrong..." + new{param1, param2});

Получить MethodInfo метода, который находится перед активным настек вызовов?

Из того, что я могу собрать, вы только планируете использовать это, чтобы регистрировать ошибку, когда выдается исключение.В этом случае я бы на время остановился на вашем решении на основе StackFrame, поскольку вряд ли это будет происходить очень часто.Когда выйдет C # 5, вы сможете использовать атрибут [CallerMemberName]:

public static void Error(Exception ex, [CallerMemberName] string caller = "") {
    ...
}

Слово мудрому

Хорошей практикой является ограничение количества мест, в которых выловить исключения как это.Обычно лучше просто позволить исключению пройти по цепочке до последнего возможного момента, когда вы можете изящно сообщить пользователю о том, что произошло непредвиденно, и записать ошибку в этот момент.Это дает вам гораздо более информативную трассировку стека на самом Исключении.Если вы хотите передать дополнительную информацию в какой-то момент, просто включите исключение во внутреннее исключение и добавьте следующее:

catch(Exception ex)
{
   throw new Exception("Something went wrong..." + new{param1, param2}, ex);
}
0 голосов
/ 30 ноября 2011

Ни один из них. Я бы пошел с решением перехвата, используя что-то вроде замка Виндзор. Тогда очень легко получить параметры для метода, который был перехвачен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...