我习惯于在不使用Linq或Lambda语句的情况下编写C#,但我想提高自己的理解力。我的代码看起来像用C#2.0编写,带有foreach
循环:
List<string> strings = new List<string>();
strings.Add("1");
strings.Add("blah");
List<int> ints1 = new List<int>();
foreach (string x in strings)
{
int r;
if (int.TryParse(x, out r))
ints1.Add(r);
}
它正在执行一项简单的任务 - 从字符串列表中填充一个int列表,忽略任何实际不存在的int。 在我有限的经验中,Linq和Lambda的陈述似乎能够将foreach语句简化为非常简洁和可读的方式来做同样的事情。所以我想我会尝试使用C#3.0这个小小的。但我能想到的最好的是:
IEnumerable<int> ints2 = strings.Select(x =>
{
int r;
if (int.TryParse(x, out r))
return r as int?;
return null;
})
.Where<int?>(x => x != null)
.Select(x => x.Value);
呸!我找不到在一次调用中将转换和过滤结合起来的方法,最终看起来比原来的更糟糕。有没有办法做到这一点,还是我应该坚持我的foreach?
编辑:总结一下调查结果,如果您使用的是.NET 4.0,有两种方法可以使用LINQ:
IEnumerable<int> ints1 = strings
.Where(x => new Int32Converter().IsValid(x))
.Select(x => int.Parse(x));//Broken in .NET 3.5 due to bug in Int32Converter
IEnumerable<int> int2 = strings
.Where(x => { int r; return int.TryParse(x, out r); })
.Select(x => int.Parse(x));
第二种方法更快,并且在.NET 3.5中没有被破坏。并且根据已接受的答案而不是foreach,可能没有理由不将其置于扩展方法中。
有关Int32Converter
错误,请参阅connect issue。
答案 0 :(得分:3)
你可以用简单的linq
来做到这一点int num = 0;
var ints = (from str in strings
where int.TryParse(str,out num)
select num).ToList();
答案 1 :(得分:3)
使用 LINQ 获取int
的另一种方法是:
var integerList =
strings
.Where(x => new Int32Converter().IsValid(x))
.Select(x => int.Parse(x));
[更新]
我使用LINQPad做了performance test。
这里我报告代码和结果:
void Main()
{
List<string> strings = new List<string>() { "1", "a", "3", "b" };
Func<IEnumerable<string>, IEnumerable<int>> func1 =
list => list.Where(x => new Int32Converter().IsValid(x)).Select(x => int.Parse(x));
Func<IEnumerable<string>, IEnumerable<int>> func2 =
list => { var ret = 0; return list.Where(x => int.TryParse(x, out ret)).Select(x => ret); };
Benchmark
.For(1000)
.Execute("Int32Converter", () => func1(strings).Iterate())
.Execute("int.TryParse", () => func2(strings).Iterate())
.Report(4).Dump();
}
int.TryParse
比Int32Converter
快约165倍。
[更新2]
TypeConverter.IsValid
类继承的Int32Converter
包含在.NET 4.0中修复的bug。
答案 2 :(得分:1)
就像那样:
from s in strings
let parsed = s.TryParseInt32()
where parsed != null
select parsed.Value;
public static int? TryParseInt32(this string str)
{
int i;
if (!int.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
return null;
return i;
}
TryParseInt32扩展名是可重用的。
这是另一种选择:
from s in strings
select s.TryParseInt32() into parsed
where parsed != null
select parsed.Value;
答案 3 :(得分:1)
我得到了一些这样的辅助方法
public static class CollectionUtilities {
public static IEnumerable<int> ParseInt32(this IEnumerable<string> source) {
return ParseInt32(source, NumberStyles.Integer, null);
}
public static IEnumerable<int?> TryParseInt32(this IEnumerable<string> source) {
return TryParseInt32(source, NumberStyles.Integer, null);
}
public static IEnumerable<int> ParseInt32(this IEnumerable<string> source, IFormatProvider provider) {
return ParseInt32(source, NumberStyles.Integer, provider);
}
public static IEnumerable<int?> TryParseInt32(this IEnumerable<string> source, IFormatProvider provider) {
return TryParseInt32(source, NumberStyles.Integer, provider);
}
public static IEnumerable<int> ParseInt32(this IEnumerable<string> source, NumberStyles style) {
return ParseInt32(source, style, null);
}
public static IEnumerable<int?> TryParseInt32(this IEnumerable<string> source, NumberStyles style) {
return TryParseInt32(source, style, null);
}
public static IEnumerable<int> ParseInt32(this IEnumerable<string> source, NumberStyles style, IFormatProvider provider) {
if (source == null)
throw new ArgumentNullException("source");
return ParseInt32Iterator(source, style, provider);
}
public static IEnumerable<int?> TryParseInt32(this IEnumerable<string> source, NumberStyles style, IFormatProvider provider) {
if (source == null)
throw new ArgumentNullException("source");
return TryParseInt32Iterator(source, style, provider);
}
private static IEnumerable<int> ParseInt32Iterator(this IEnumerable<string> source, NumberStyles style, IFormatProvider provider) {
foreach (string element in source) {
yield return int.Parse(element, style, provider);
}
}
private static IEnumerable<int?> TryParseInt32Iterator(this IEnumerable<string> source, NumberStyles style, IFormatProvider provider) {
foreach (string element in source) {
int value;
bool success = int.TryParse(element, style, provider, out value);
yield return success ? (int?)value : null;
}
}
}
使用这些帮助程序,您的代码将如下所示:
var ints = strings.TryParseInt32().Where(x => x != null).Select(x => x.Value).ToList();
答案 4 :(得分:1)
我将按如下方式定义自己的扩展方法:
public static IEnumerable<int> AsIntegers (this IEnumerable<string> strings) {
foreach (var s in strings) {
int r; if (int.TryParse (s, r)) yield return r;
}
}
...
List<int> intList = new List (stringList.AsIntegers ());
或
var intList = stringList.AsIntegers ().ToList ();