获取抛出异常的方法名

Get Method name that threw exception

我知道。已经有人问过类似的问题了。

但我还没有从中得到确切的解决方案。

我有一个按钮单击事件,其中有一个方法 FillCombo()

按钮点击事件

private void button1_Click(object sender, EventArgs e)
{
      try
      {
          cmbTemplates.Items.Clear();
          lstFiles.Clear();
          FillCombo();                
      }
      catch (Exception ex)
      {
           MethodBase site = ex.TargetSite;
           Log(ex.ToString(), site == null ? null : site.Name);                
      }
}

我调试的时候发现异常发生在FillCombo()方法。之后我得到 site.Name 的值作为 WinIOError 而不是 FillCombo.

我尝试了另一种方法 GetExecutingMethodName() Chris GesslerHow to get the name of the method that caused the exception 问题中得到了回答。所以我尝试使用 GetExecutingMethodName() 方法

发送导致异常的方法的名称
Log(ex.ToString(), GetExecutingMethodName());

但我得到的结果是 System.Windows.Forms.Control.OnClick 而不是 FillCombo

如何获取导致异常的方法的实际名称?

了解 "the method that threw the exception" 的含义很重要。当异常发生时,有一个特定的方法在实际执行。仅仅因为在异常发生前的某个时间点,您调用了自己的 FillCombo() 方法,这并不意味着是抛出异常的方法。

然而,FillCombo() 方法(在您关心的情况下)将出现在堆栈跟踪中。这就是记录整个堆栈跟踪很有用的原因。事实上,我通常只是记录整个 Exception 对象(即 ex.ToString(),或者只是将异常对象传递给 string.Format() 或类似的对象,它将为您调用 ToString()。这将包括异常类型、消息、整个堆栈跟踪,甚至内部异常信息(如果存在)。


您从另一个问题中获得的 GetExecutingMethodName() 方法的代码并不是真正有用的恕我直言。您会注意到,它真正做的是爬取 当前 执行位置的堆栈跟踪,寻找第一个在 [= 所在的类型以外的类型中声明的方法17=] 已声明。

这对您的目的来说是错误的,原因有二:

  1. 您似乎在声明 Click 事件处理程序的同一 class 中声明了该方法。这意味着事件处理程序方法被忽略,因此您得到该方法的调用者,即 Control.OnClick() 方法(即实际引发事件的方法)。

坦率地说,我觉得那个特定的答案很奇怪,因为 .NET 已经提供了一个 API 来检索当前正在执行的方法的 MethodInfoMethodBase.GetCurrentMethod。这比 Chris Gessler 写的代码更可靠。

  1. 更有问题,你没有机会在抛出异常的地方调用这个方法!充其量(即,即使您处理声明辅助方法的位置的问题),所有调用都会告诉您您在 button1_Click() 方法中。但是您已经知道了,因为您正在编写的处理异常的代码就在该方法中。


如果您想知道在异常发生之前调用的当前正在执行的方法中的方法的名称,您可以结合这两种技术:获取当前正在执行的方法的名称,然后将其传递给同时接受这两个方法的方法和来自 Exception 对象的堆栈跟踪字符串,并让该方法解析堆栈跟踪字符串以找到跟踪中当前正在执行的方法之前的帧。

这有点痛苦,但可以做到。这是一个示例(简单的概念验证控制台程序):

static void Main(string[] args)
{
    try
    {
        CallForException();
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception occurred calling {0} method", GetCallForExceptionThisMethod(MethodBase.GetCurrentMethod(), e));
    }
}

private static string GetCallForExceptionThisMethod(MethodBase methodBase, Exception e)
{
    StackTrace trace = new StackTrace(e);
    StackFrame previousFrame = null;

    foreach (StackFrame frame in trace.GetFrames())
    {
        if (frame.GetMethod() == methodBase)
        {
            break;
        }

        previousFrame = frame;
    }

    return previousFrame != null ? previousFrame.GetMethod().Name : null;
}

private static void CallForException()
{
    DoActualException();
}

private static void DoActualException()
{
    throw new NotImplementedException();
}

最后,请记住,由于方法内联和其他优化,即使是完整的堆栈跟踪也可能有一些不规则之处,包括甚至没有抛出异常的方法的实际名称。这是记录整个 Exception 对象通常更有用的另一个原因;上下文越多,您就越有可能重建发生的事情。

.net 支持从异常中获取堆栈跟踪信息。您可以通过检查第一帧(来源)来过滤掉方法(及其名称)。

new StackTrace(ex).GetFrame(0).GetMethod().Name

这可能会为您提供与目标站点(win io)完全相同的结果,但您可以检查第一个用户代码的堆栈跟踪,或您的类型中的第一个框架,或任何您需要的。

例如,获取当前程序集中的罪魁祸首的名称:

var s = new StackTrace(ex);
var thisasm = Assembly.GetExecutingAssembly();                
var methodname = s.GetFrames().Select(f => f.GetMethod()).First(m => m.Module.Assembly == thisasm).Name;

试试这个:

var methodFullName = exception.TargetSite.ReflectedType.FullName