foreach块内的堆栈跟踪行号错误

时间:2016-09-29 08:26:17

标签: c# for-loop exception .net-4.0 stack-trace

我无法理解.net 4中的堆栈跟踪。我在控制台应用程序或IIS 7.5上托管的Web服务中运行的相同代码获得了不同的行号(都使用.net 4)。

控制台应用程序:

static void Main(string[] args)
{
    try
    {
        var test = new List<int>() { 1, 2, 3 };
        foreach (var root in test)
        {
            throw new Exception("foobar");
        }
    }
    catch(Exception e)
    {
        throw;
    }
}

当检查catch块内的“e”的堆栈跟踪行号时,我得到“8”,这是我除外的(抛出新的异常(“foobar”)行

网络服务

[WebMethod]
public void Test()
{
    try
    {
        var test = new List<int>() { 1, 2, 3 };
        foreach (var root in test)
        {
            throw new Exception("foobar");
        }
    }
    catch (Exception e)
    {
        throw;
    }
}

这是事情变得奇怪的地方 - 当检查堆栈跟踪行号时,我得到“7”,这是 foreach 块的开始。经典 for 也是如此,但如果语句可以正常工作。

有什么想法吗?

修改

关于机器学习中有关ide或运行时行与pdb之间未对齐的答案。如果我在异常之前添加无意义的行,之后添加一些无意义的行,我仍然会得到类似的行为。例如:

[WebMethod]
public string Test()
{
    try
    {
        var test = new List<int>() { 1, 2, 3 };

        var a = 1;
        var b = 2;
        var c = 3;

        foreach (var root in test)
        {
            var d = 4;
            var e = 5;
            var f = 6;
            throw new Exception("foobar" + (new System.Diagnostics.StackFrame(0, true)).GetFileLineNumber());
            var g = 7;
        }
        return null;
    }
    catch (Exception e)
    {
        return e.Message + e.StackTrace;
    }
}

此处e.StackTrace报告“12”行(foreach行)和e.Message报告行“17”,这是正确的。

1 个答案:

答案 0 :(得分:1)

这是在Windows上处理异常的结果。重新抛出同一方法中抛出的异常意味着您将丢失有关原始异常的信息,因为异常是方法作用域[1]。遗憾的是,.NET继承了这一限制,因为唯一的选择是在不依赖现有基础架构的情况下实现异常。

如果我没记错,这是在64位代码中修复的 - 确保您的控制台应用程序以64位运行,它应该按预期工作。这很可能是您的测试应用程序中一切正常的原因,但不是在您的IIS上(代码最有可能以32位运行,可能与debug=false一起运行)。 编辑:这实际上似乎并非如此。虽然涉及位数,但它很可能与JITters所做的优化有关 - 64位上的foreach报告foreach行上的异常,同时用foreach替换using GetEnumeratorthrow new ...等,或将位数更改为32位将报告重新抛出的异常。

如果要在所有代码中避免此问题,请确保永远不要重新抛出最初在同一方法中引发的异常。将throw提取到一个单独的方法(并确保它没有内联,当在当前的MS运行时AFAIK上有foreach时自动发生)应该可以正常工作。不要忘记using还包含一个非隐式finally,即{{1}}条款。

[1]毋庸置疑,这有点过于简单化了。底层(本机)结构化异常处理每个堆栈帧只保留一个异常 - 它不能跟踪原始抛出和重新抛出。如果您有兴趣深入了解SEH如何使用简单的Google搜索找到大量信息:)