.NET обработка ошибок - PullRequest
       1

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

10 голосов
/ 18 августа 2011

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

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

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

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, но меня интересует, главным образом, перехват вызова метода и параметров, которые вызвали исключение системой.

Есть идеи?

Ответы [ 7 ]

13 голосов
/ 18 августа 2011

Я предлагаю вставить значения параметров в словарь 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;
}
7 голосов
/ 18 августа 2011

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

Например:

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

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

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

1 голос
/ 18 августа 2011

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

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

Что делать, если у вас есть два или более блоков 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 не является полностью бесплатным, но его версия для сообщества будет поддерживать базовые функции, описанные выше.

1 голос
/ 18 августа 2011

Для этого:

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();
}
0 голосов
/ 26 августа 2011

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

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я работаю в программном обеспечении LogicNP, разработчик Crypto Obfuscator.

0 голосов
/ 18 августа 2011

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

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);
    }
}

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

0 голосов
/ 18 августа 2011

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

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