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

Wrong stack trace line number inside foreach block

我无法理解 .net 4 中的堆栈跟踪。我在 IIS 7.5 上托管的控制台应用程序或 Web 服务(均使用 .net)中得到不同的 ide 代码 运行 行号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;
    }
}

当检查 "e" inside catch 块的堆栈跟踪行号时,我得到“8”,这是我排除的(throw new Exception( "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 也会发生同样的情况,但是 if 语句例如工作正常。

任何 ideas?

编辑:

关于机器学习关于 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”是正确的。

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

如果我没记错的话,这在 64 位代码中已修复 - 确保您的控制台应用程序以 64 位运行,并且它应该按预期工作。这很可能是为什么在您的测试应用程序中一切正常,但在您的 IIS 上却不行(代码很可能 运行 作为 32 位,并且可能 debug=false)。 编辑: 这实际上似乎并非如此。虽然涉及位数,但它很可能与 JITter 所做的优化有关 - 64 位上的 foreach 报告 foreach 行上的异常,同时将 foreach 替换为usingGetEnumerator等,或者把bitness改成32位,rethrow会报异常。

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

[1] 不用说,这有点过于简单化了。底层(本机)结构化异常处理仅在每个堆栈帧中保存一个异常 - 它无法跟踪原始抛出和重新抛出。如果您有兴趣进行更深入的研究,您会发现大量关于 SEH 如何使用简单 Google 搜索的信息:)