Обработка ошибок .NET

Я писал .NET-приложения и был впечатлен обработкой ошибок, включенными в фреймворк.

При обнаружении ошибки, которая была вызвана процессами или где-то в коде, мне нравится включать сообщение (ex.Message, которое обычно довольно общее), а также стек (t21), который помогает отслеживать проблема возвращается в конкретное место.

Для простого примера предположим, например, что мы записываем числа в журнал в методе:

public void ExampleMethod(int number){
    try{
        int num = number
        ...open connection to file
        ...write number to file
    }
    catch(Exception ex){
        .... deal with exception (ex.message,ex.stacktrace etc...)
    }
    finally{
    ...close file connection
    }
}

Есть ли способ увидеть вызванный метод (в данном случае ExampleMethod) с определенным номером, который был передан, что потенциально может привести к сбою вызова метода? Я считаю, что вы могли бы зарегистрировать это, возможно, в блоке catch, но мне интересно в основном поймать вызов метода и параметры, которые заставляют систему генерировать исключение.

Любые идеи?

Ответ 1

Я предлагаю вводить значения параметров в словарь Data исключение .

public void ExampleMethod(int number) {
try {
    int num = number
    ...open connection to file
    ...write number to file
}
catch(Exception ex) {
    ex.Data["number"] = number;
    //.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally {
    //...close file connection
}

Другим преимуществом этого метода является то, что вы можете заполнить параметры в блоке catch, затем повторно выбросить исключение и записать его в другое место без потери трассировки стека, например.

catch(Exception ex) {
    ex.Data["number"] = number;
    throw;
}

Ответ 2

Если вы хотите узнать значение параметров в своем методе, то для этого есть только один способ - IMO, вам нужно переупаковать исключение с данными.

Например:

 int param1 = 10;
 string param2 = "Hello World";

 try
 {
     SomeMethod(param1, param2)
 }
 catch(SomeExpectedException e)
 {
      throw new MyParameterSensitiveException(e, param1, param2);
 }

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

Ответ 3

Чтобы сделать это:

public void MyProblematicMethod(int id, string name)
{
    try
    {
        object o = null;
        int hash = o.GetHashCode(); // throws NullReferenceException
    }
    catch (Exception ex)
    {
        string errorMessage = SummarizeMethodCall(MethodBase.GetCurrentMethod(), id, name);
        // TODO: do something with errorMessage
    }
}

... и получите следующее:

"MyProblematicMethod invoked: id = 1, name = Charlie"

... вы можете сделать что-то вроде этого:

public static string SummarizeMethodCall(MethodBase method, params object[] values)
{
    var output = new StringBuilder(method.Name + " invoked: ");
    ParameterInfo[] parameters = method.GetParameters();
    for (int i = 0; i < parameters.Length; i++)
    {
        output.AppendFormat("{0} = {1}",
            parameters[i].Name,
            i >= values.Length ? "<empty>" : values[i]
        );
        if (i < parameters.Length - 1)
            output.Append(", ");
    }
    return output.ToString();
}

Ответ 4

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

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

Что делать, если у вас есть два или более блока try..catch? Затем у вас есть два блока кода для обновления. Определенно не рефакторинг.

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

Одним из таких инструментов для облегчения этого является продукт под названием PostSharp.

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

LoggerAttribute.cs -

[Serializable]
public class LoggerAttribute : OnExceptionAspect
{
  public override void OnException(MethodExecutionEventArgs eventArgs)
  {
    Console.WriteLine(eventArgs.Method.DeclaringType.Name);
    Console.WriteLine(eventArgs.Method.Name);
    Console.WriteLine(eventArgs.Exception.StackTrace);

    ParameterInfo[] parameterInfos = eventArgs.Method.GetParameters();
    object[] paramValues = eventArgs.GetReadOnlyArgumentArray();

    for (int i = 0; i < parameterInfos.Length; i++)
    {
      Console.WriteLine(parameterInfos[i].Name + "=" + paramValues[i]);
    }

    eventArgs.FlowBehavior = FlowBehavior.Default;
  }
}

Затем вы украшаете свои классы с помощью LoggerAttribute:

[Logger]
public class MyClass
{
  public void MyMethod(int x, string name)
  {
      // Something that throws an exception
  }
}

Все, что генерирует исключение в MyMethod, приведет к выполнению метода OnException.

Существуют две версии PostSharp. Версия 1.5 бесплатна и открыта под GPL и ориентирована на .NET 2.0. PostSharp 2.0 не является полностью бесплатным, но его версия сообщества будет поддерживать базовые функции, описанные выше.

Ответ 5

Вы можете создать класс, наследующий Exception, и добавить к нему некоторые аргументы, чтобы вы могли передать ему номер.

Ответ 6

Вы можете получить имя метода и параметры, подобные этому,

try
{
    int a = 0;
    int i = 1 / a;
}
catch (Exception exception)
{
    StackTrace s = new StackTrace(exception);
    StackFrame stackFrame = s.GetFrame(s.FrameCount - 1);
    if (stackFrame != null)
    {
        StringBuilder stackBuilder = new StringBuilder();
        MethodBase method = stackFrame.GetMethod();
        stackBuilder.AppendFormat("Method Name = {0}{1}Parameters:{1}", method.Name, Environment.NewLine);

        foreach (ParameterInfo parameter in method.GetParameters())
        {
            stackBuilder.AppendFormat("{0} {1}", parameter.ParameterType.FullName, parameter.Name);
            stackBuilder.AppendLine();
        }

        // or use this to get the value
        //stackBuilder.AppendLine("param1  = " + param1);
        //stackBuilder.AppendLine("param2  = " + param2);
    }
}

Я не уверен, что вы можете получить значения параметров непосредственно из стека, как отладчик.

Ответ 7

Автоматическая обработка исключений от Crypto Obfuscator может делать то, что вам нужно.

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я работаю в LogicNP Software, разработчике Crypto Obfuscator.