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

我无法理解.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也是如此,但if语句例如工作正常.

有任何想法吗?

编辑:

关于机器学习中有关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)的原因.编辑:事实上似乎并非如此.虽然涉及到位数,但它很可能与JITters所做的优化有关 – 64位上的foreach报告foreach行上的异常,同时用using,GetEnumerator等替换foreach,或者将bitness更改为32 -bit将报告重新抛出的异常.

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

[1]毋庸置疑,这有点过于简单化了.底层(本机)结构化异常处理仅为每个堆栈帧保留一个异常 – 它无法跟踪原始抛出和重新抛出.如果你有兴趣深入研究,你会发现很多关于SEH如何与简单的谷歌搜索一起工作的信息:)

点赞