6

我喜欢 LINQ 语句的表达性语法和其他方便的功能。但是,我发现有时调试它们很麻烦。具体来说,当我对集合运行 LINQ 语句并且集合中的一个元素导致异常时,我如何才能确定问题输入是什么以及问题来自哪里?

想象一下,我有一个包含 1000 个实数的文本文件:

0.46578
12.314213
1.444876
...

我正在阅读List<string>它并将其加载到更具体的数据结构中:

var file_contents = File.ReadAllLines("myfile.txt");
var data = file_contents.Select(s => double.Parse(s));

现在,对于这个特定的输入,我没有仔细查看它,结果发现第 876 行包含(显示的行号):

875    5.56786450
876    Error: Could not calculate value.
878    0.0316213

无论出于何种原因(也许该文件是由出现故障的脚本生成的)。我的 LINQ 方法链当然会抛出异常。问题是,我如何确定列表中的哪个元素导致了异常,它的值是多少?

为了澄清,如果我使用了一个for循环:

var data = new List<double>();
foreach(var row in file_contents)
{
    var d = double.Parse(row);
    data.Add(d);
}

然后异常会突出显示调用的字符串double.Parse,我可以将鼠标悬停在上面row以轻松查看问题输入是什么。

当然,我可以使用 Resharper 将我的 LINQ 语句转换为 for 循环,然后对其进行调试,但有更好的方法吗?

4

5 回答 5

3

在 lambda 函数上放置条件断点,条件为 s.StartsWith("5.56")。您只需将光标放在 lambda 上并按 F9。假设您使用的是视觉工作室。

于 2013-05-01T13:08:29.503 回答
2
var data = file_contents.Select(s => {
    try
    {
       return double.Parse(s);

    }
    catch
    {
       throw; //breakpoint?
    }
});
于 2013-05-01T13:08:37.997 回答
2

免责声明:我为 OzCode 工作

使用 Visual Studio 无法进行 LINQ 调试。我建议您尝试使用 OzCode。

这是您的代码在调试时的样子(第 6 项中的异常)。 调试 LINQ 异常

您可以通过调查传递给Select子句的项目来判断是哪个项目导致了异常- 由于最后一个项目触发了异常 - 很容易找到有问题的值。

如果您有兴趣可以尝试 OzCode 的 LINQ 调试 - 我们刚刚启动了EAP

于 2016-06-14T14:11:37.927 回答
0

我不确定你为什么不喜欢foreach这里的循环。无论如何,LINQ 在内部使用它,并且您已经意识到使用 LINQ 有一些优点和缺点,而调试是缺点之一。

我可能会将 LINQ 与以下内容混合使用foreach并最终得到以下结果:

// read all lines from file //
var file_contents = File.ReadAllLines("myfile.txt");

// set initial data list length to number of lines for better performance
var data = new List<double>(file_contents.Length);

// list for incorrect line numbers
var incorrectRows = new List<int>();

foreach (var x in file_contents.Select((s, i) => new {s, i}))
{
    // x.s - line string
    // x.i - line number

    double value;
    if (double.TryParse(x.s, out value))
        data.Add(value);        // add value, which was OK
    else
        incorrectRows.Add(x.i); // add index of incorrect value
}

这将完全防止异常,并为您提供所有不正确值的行号。它也只迭代file_contents一次,每个值只被解析一次。

于 2013-05-01T19:37:47.593 回答
0

我会亲自使用 tryparse 。

        var data = new List<string>
            {
                "0.46578",
                "12.314213",
                "Error: Could not calculate value.",
                "1.444876",
            };
        double d;
        var good = data.Where(s => Double.TryParse(s, out d)).Select(Double.Parse);
        var bad = data.Where(s => !Double.TryParse(s, out d)).Select(x => new
            {
                key = data.IndexOf(x),
                value = x
            }).ToDictionary(x => x.key, x => x.value);


        textBox1.AppendTextAddNewLine("Good Data:");
        WriteDataToTextBox(good);

        textBox1.AppendTextAddNewLine(String.Format("{0}{0}Bad Data:", Environment.NewLine));
        WriteDataToTextBox(bad);

AppendTextAddNewLine 只是我为我的小概念验证测试程序编写的一个扩展方法

    public static void AppendTextAddNewLine(this TextBox textBox, string textToAppend)
    {
        textBox.AppendText(textToAppend + Environment.NewLine);
    }

编辑

WriteDataToTextbox 是一种将IEnumerble<T>输出写入文本框的通用方法。

    void WriteDataToTextBox<T>(IEnumerable<T> data )
    {
        foreach (var row in data)
        {
            textBox1.AppendTextAddNewLine(row.ToString());
        }
    }

忘了把输出放在这里,所以我想我应该这样做。它显示了坏数据的索引和导致问题的数据本身。

Good Data:
0.46578
12.314213
1.444876


Bad Data:
[2, Error: Could not calculate value.]
于 2013-05-01T15:51:26.473 回答