2013-05-01 17 views
6

我喜欢LINQ语句以表达语法和其他方便的功能。不过,我觉得有时候调试起来很麻烦。具体来说,当我在集合上运行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-loops,然后调试它们,但有没有更好的方法?

+0

什么样的LINQ的这是什么? EF或LINQ to SQL? – 2013-05-01 19:47:10

回答

3

将条件断点放在条件为s.StartsWith(“5.56”)的lambda函数上。你只需要把你的光标放在lambda上并按下F9。假设你正在使用visual studio。

2
var data = file_contents.Select(s => { 
    try 
    { 
     return double.Parse(s); 

    } 
    catch 
    { 
     throw; //breakpoint? 
    } 
}); 
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.] 
+0

非常感谢您评论的建设性。既然你有更好的方法,你会发布自己的答案,这不是“你见过的最糟糕的LINQ用法”。 – Charles380 2013-05-01 19:21:19

+0

这里主要的问题是,你的解决方案迭代数据''2 + I'次,其中'I'是不正确的行数 - 'IndexOf'从每次调用时开始迭代集合。你也可以每行解析至少两次,其中大部分是三次(两个'TryParse'和一个'Parse'以获得更好的值)。这不是很有效,是吗?我发布了我的答案,随时批评它! – MarcinJuraszek 2013-05-01 19:42:58

+0

让我来告诉你如何直接从你的评论中获得代码,使代码更加高效,从而避免非构造性部分。 “你的解决方案迭代了数据2 + I次,其中我是不正确的行数 - IndexOf每次调用时都会从头开始迭代该集合,并且每行至少解析两次,其中大部分解析三次(两个TryParse和一个解析为好值)。已经有选择过载索引提供..使用.Select((s,i)=>我是indexOf“ – Charles380 2013-05-01 20:57:35

0

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

我可能会用foreach混合LINQ和具有下列结束:

// 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只有一次,每个值只被解析一次。

1

声明:我OzCode工作

LINQ调试很难交界不可能使用Visual Studio。我建议你尝试使用OzCode。

这就是您的代码在调试时的样子(第6项中的例外)。 Debugging LINQ exception

你可以知道哪些项目通过调查认为那里传递到选择子句中的项目引起异常 - 和自上次触发一个例外 - 它很容易找到有问题的值。

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