【问题标题】:.NET error handling.NET 错误处理
【发布时间】:2011-08-26 15:04:06
【问题描述】:

我一直在编写 .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 块中,但我主要感兴趣的是捕获导致系统抛出异常的方法调用和参数。

有什么想法吗?

【问题讨论】:

  • 最后调用的导致异常的方法已经是堆栈跟踪的一部分...我是否遗漏了您的问题?
  • @tejs - 他也想要传递给方法的参数值。

标签: c# .net exception-handling


【解决方案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;
}

【讨论】:

  • 我喜欢这种方法。我总是对 Data 属性很少使用感到惊讶......
  • 哇我从来没有听说过这个功能
  • 一个警告,克里斯蒂安。 “不丢失堆栈跟踪”并不是 100% 准确的。当您重新抛出异常时,即使使用throw;,堆栈跟踪位置在该代码块中 也会被重置。因此,如果您正在分发 PDB 文件以获取堆栈跟踪中的行号,那么如果您重新抛出,某些行号将会关闭。
【解决方案2】:

如果您想知道方法中参数的值,那么只有一种方法,IMO,可以做到这一点——您需要用数据重新打包异常。

例如:

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

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

您基本上将原始异常重新包装为另一个异常的内部异常,并额外提供您用于调用该方法的参数。然后您可以通过某种方式对其进行检查以找出问题所在。

【讨论】:

  • Exception.Data 属性提供了存储异常详细信息的能力。当系统异常提供必要的实现时,我认为不需要自定义异常。调用记录器 (MyLogger.Log(exceptionInstance)) 就足够了。
【解决方案3】:

接受的答案和所描述的许多解决方案都可以正常工作,但您正在做的是用稍微不同的代码块乱扔您的源代码,具体取决于您的方法签名中的参数。

当需要添加新参数时,您需要记住更新处理程序以添加新参数。或者,如果您删除了一个参数,那么您需要记住从您的异常处理程序中删除该参数。

如果您有两个或更多 try..catch 块怎么办?然后你现在有两个代码块来保持最新。绝对不是重构友好的。

另一种方法是使用称为Aspect Oriented Programming 的技术删除日志记录代码。

一个这样的工具来促进这一点是一个名为PostSharp的产品。

使用 PostSharp,您可以编写一个记录器,在引发异常时调用该记录器,而无需杂乱的方法和参数特定代码。例如(使用 PostSharp 1.5 版):

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 并非完全免费,但其社区版将支持上述基本功能。

【讨论】:

    【解决方案4】:

    为了做到这一点:

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

    【讨论】:

      【解决方案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 的 Automatic Exception Handling 可以满足您的需求。

          异常报告包括所有相关信息,包括完整的堆栈跟踪信息以及所有方法参数和局部变量的值,以及系统信息、异​​常时间、内部版本号和可选的开发人员定义的自定义数据,如日志文件、屏幕截图等。

          免责声明:我为 Crypto Obfuscator 的开发商 LogicNP Software 工作。

          【讨论】:

            猜你喜欢
            • 2014-05-27
            • 1970-01-01
            • 1970-01-01
            • 2013-12-12
            • 2016-06-22
            • 2016-07-16
            • 1970-01-01
            • 1970-01-01
            • 2019-05-21
            相关资源
            最近更新 更多