【问题标题】:Debugging a foreach loop in C#: what iteration is this?在 C# 中调试 foreach 循环:这是什么迭代?
【发布时间】:2011-03-18 14:36:08
【问题描述】:

除了设置一个调试变量并在每次启动 foreach 时递增它之外,当您在连接了 Visual Studio 调试器的情况下闯入时,有没有办法告诉这是第 X 次通过循环?

我猜这将是 Visual Studio 的一个功能,如果有的话,而不是添加到编译代码中的东西。

【问题讨论】:

  • foreach 转换为带有索引的for 需要两秒钟。有什么大不了的?
  • @Josh - 设置带有命中计数的断点也需要 2 秒。调试器是我们的朋友,我们需要学习如何使用它。
  • @kirk 你要浪费多少次两秒钟才意识到应该从一开始就使用索引变量?
  • @Josh,如果他们想要确定调试器中的索引,您实际上是在建议绝对避免使用foreach 循环吗?
  • 我可能已经处于一个耗时的运行过程中,在这种情况下我必须重新开始,这将花费超过 2 秒的时间。

标签: c# .net visual-studio debugging foreach


【解决方案1】:

在循环内设置断点,然后在断点上右击设置条件。您也可以在调试时右键单击以查看命中计数,并根据需要将其重置。您可以设置一个布尔表达式,当断点命中时进行评估以有条件地中断(或只是跳过)。

【讨论】:

  • 刚试过这个(VS 2015 Pro),不,它不起作用。如果条件不满足,这会抑制命中计数的增加,而不仅仅是破坏。
【解决方案2】:

您还可以使用 Visual Studio 的 即时窗口,它允许您在调试时针对范围内的任何变量编写 C# 表达式。使用 List.IndexOf() 方法,如下所示:

【讨论】:

  • 简单易行!!
  • 有用!当所有对象都是唯一的并且在列表/数组中时......但请注意,可能会有重复,并且可能 foreach 已经结束list.Where(x => x != null)。这实际上是对“当前对象的第一个索引是什么”而不是“当前迭代的索引是什么”的答案。
【解决方案3】:

扩展 Garo Yeriazarian 的答案...

一种快速而肮脏的方式,无需重新编译。示例代码:

    var ints = new[] {5, 6, 0, 1};

    foreach (var i in ints)
    {
        Debug.WriteLine(100 / i);
    }

在循环之前添加一个断点,在循环内部添加一个。当第一个被击中并且你想开始计数时,设置一个 Hit Count 条件:

设置一些大的命中计数条件并重置计数器并继续。然后当异常或其他什么触发时,您可以再次检查“当前命中计数”。

【讨论】:

  • 此功能似乎已在 VS 2015 中删除 :(
【解决方案4】:

这是之前的 StackOverflow 问题,这似乎是您要查找的内容: get-index-of-current-foreach-iteration

从该链接引用的答案:

Foreach 用于迭代实现 IEnumerable 的集合。 它通过在集合上调用 GetEnumerator 来做到这一点,这将 返回一个枚举器。

这个枚举器有一个方法和一个属性:

  • MoveNext()
  • 当前

Current 返回 Enumerator 当前所在的对象,MoveNext 将 Current 更新为下一个对象。

显然,索引的概念与 枚举,无法完成。

因此,大多数集合都可以使用 索引器和 for 循环构造。

与这种情况相比,我更喜欢在这种情况下使用 for 循环 使用局部变量跟踪索引。

【讨论】:

    【解决方案5】:

    也许你可以使用断点命中计数。不完全是您想要的,但可能会有所帮助。

    在这种情况下,您不想使用for 循环还有什么严重的原因。

    【讨论】:

    • 1.如果一个人不能使用它,那么拥有 foreach 有什么意义呢? 2. 目前尚不清楚 OP 是否正在尝试使用已经运行的程序来确定循环已经迭代了多少次。这是我试图解决的问题 - 运行绝对需要很长时间,我想看看它到目前为止运行了多少次,以便估计需要多长时间。停止程序、更改代码并重新开始是在这种情况下最不想做的事情!
    【解决方案6】:

    2017 年 2 月更新,六年后 - 下面提到的扩展现在称为 OzCode。该功能现在称为Foresee,但仅在 VS2013 中受支持。

    我还觉得这可能是一个非常有用的功能,因此我将它创建为我为 Visual Studio 调试体验制作的商业扩展的一部分,称为 BugAid。

    当您在 foreach 循环中时,该扩展程序会准确显示您所处的迭代:

    当您单击“Iteration x of y”按钮时,您会看到一个新窗口,其中显示了完整的项目列表,并突出显示了您在循环中的当前位置(此列表仅在评估集合时显示调试器不会造成任何副作用)。

    打开 Foreach 可视化窗口后,您甚至可以右键单击任何即将出现的项目并选择“跳到项目”,向前运行直到您点击该项目(这可以让您免于手动设置和搞乱命中计数断点):

    【讨论】:

    • 我怎么没听说过这个扩展!!!???如果它的表现和看起来一样好,它绝对会出现在我的“购买”清单上!
    • 谢谢 :) 您可能没有听说过,因为我们昨天刚刚发布了这些功能 ;)。我们仍处于测试阶段,所以请告诉我们您遇到的任何问题!
    • @OmerRaviv:你现在提到的扩展是 OzCode 吗?您可能想要更新您的旧链接,现在他们正在向接管域名的邪恶网站提供谷歌服务。
    • @BenVoigt 感谢您的提醒!已更新。
    【解决方案7】:

    假设你的代码是

    foreach (String line in lines){
        Console.WriteLine(line);//breakpoint here
    }
    

    foreach循环中放一个断点,启动“立即窗口”并执行以下代码Array.IndexOf(lines, line);

    【讨论】:

    • 这是适用于数组且无需任何代码更改的最简单的解决方案。您只需要注意您的列表不包含重复项,否则它将无法正常工作(并且总是在第一次出现时打印您)。
    【解决方案8】:

    [在 VS 2017 中] 我是这样做的:

    1. 在 foreach 循环内设置断点
    2. 右键单击断点并选择“操作”
    3. 在文本框中,输入以下内容:$FUNCTION {list.Items.IndexOf(item)} 其中“list”是您的列表名称,“item”是当前项目
    4. 继续运行代码并观察输出窗口

    【讨论】:

    • 投反对票,请解释为什么这个解决方案不起作用。
    【解决方案9】:

    Visual Studio 2017 中的命中数:

    1. foreach 循环内的任意位置设置断点。
    2. 右键单击断点并单击“条件...”。

    1. 选中“条件”框,将下拉菜单切换到“命中计数”并编辑您的命中计数设置。

    1. 暂停时,将鼠标悬停在断点上以查看您的设置和到目前为止的命中计数。

    1. 不要忘记,当您在同一会话中第二次进入循环时,您的命中计数不会自动重置为零。 ;-) 但您可以手动重置它:

    【讨论】:

      【解决方案10】:

      您是否尝试过在调试中使用断言?调试器将在您代码中的确切位置启动:
      例如: System.Diagnostics.Debug.Assert (myValue >=0)

      【讨论】:

        【解决方案11】:

        如果您正在迭代的任何内容都支持 IndexOf() 方法,则您不必设置调试变量。

        就像这个例子:

        foreach (var i in myList)
        {
            reportprogress(myList, i);
        
            //Do stuff
        }
        
        private void reportprogress<T>(List<T> l, T i)
        {
            progressBar1.Value = ((l.IndexOf(i)) * 100) / l.Count;
            Application.DoEvents();
        }
        

        【讨论】:

        • 它可能导致 n^2 行为(因此对于长列表来说是灾难性的)。
        • 如果同一个项目多次出现在列表中怎么办? (我有一个包含 3000 多个元素的 byte[]...应用鸽洞原理)
        猜你喜欢
        • 2019-03-26
        • 1970-01-01
        • 1970-01-01
        • 2021-10-14
        • 2013-03-23
        • 2014-05-29
        • 2016-01-03
        • 1970-01-01
        • 2011-04-29
        相关资源
        最近更新 更多