我认为解释我的问题的最好方法是使用简短的(通用的)linq-to-objects代码示例:
IEnumerable<string> ReadLines(string filename)
{
string line;
using (var rdr = new StreamReader(filename))
while ( (line = rdr.ReadLine()) != null)
yield return line;
}
IEnumerable<int> XValuesFromFile(string filename)
{
return ReadLines(filename)
.Select(l => l.Substring(3,3))
.Where(l => int.TryParse(l))
.Select(i => int.Parse(i));
}
请注意,此代码会解析整数两次。我知道我错过了一种明显的简单方法来安全地消除其中一个呼叫(因为我之前已经完成了)。我现在找不到它。我怎么能这样做?
答案 0 :(得分:9)
怎么样:
int? TryParse(string s)
{
int i;
return int.TryParse(s, out i) ? (int?)i : (int?)null;
}
IEnumerable<int> XValuesFromFile(string filename)
{
return from line in ReadLines(filename)
let start = line.Substring(3,3)
let parsed = TryParse(start)
where parsed != null
select parsed.GetValueOrDefault();
}
如果您愿意,您可以组合第二/第三行:
return from line in ReadLines(filename)
let parsed = TryParse(line.Substring(3,3))
GetValueOrDefault
的选择是因为这会跳过执行(int)
或.Value
执行的验证检查 - 即它(稍微有点)更快(我们已经检查它不是null
)。
答案 1 :(得分:3)
它不是很漂亮,但你可以这样做:
return ReadLines(filename)
.Select(l =>
{
string tmp = l.Substring(3, 3);
int result;
bool success = int.TryParse(tmp, out result);
return new
{
Success = success,
Value = result
};
})
.Where(i => i.Success)
.Select(i => i.Value);
当然,这主要是将工作推入lambda,但它确实提供了正确的答案,只需一次解析(但额外的内存分配)。
答案 2 :(得分:3)
我想我会用这样的东西:
IEnumerable<O> Reduce<I,O>(this IEnumerable<I> source, Func<I,Tuple<bool, O>> transform )
{
foreach (var item in source)
{
try
{
Result<O> r = transform(item);
if (r.success) yield return r.value;
}
catch {}
}
}
ReadLines().Reduce(l => { var i; new Tuple<bool, int>(int.TryParse(l.Substring(3,3),i), i)} );
我不是很喜欢这个,因为我已经on the record as not liking using tuples in this way了。不幸的是,除了滥用异常或将其限制为引用类型(其中null
被定义为转换失败)之外,我没有看到很多其他选择,这两种情况都不是更好。