2012-05-28 50 views
3

我习惯于在不使用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); 
     } 

它做一个简单的任务 - 从一个字符串列表填充整数列表,忽略任何实际并非整数。 在我有限的经验中,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); 

Yu!我无法找到一种方法在一次调用中将转换和过滤结合起来,并最终看起来比原来差得多。有没有办法做到这一点,或者我应该坚持我的foreach?

编辑:总结发现,这里有2种方式,如果你使用的是.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

+3

我喜欢'foreach'。 –

回答

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(); 
+0

这是可读和简洁的。但是,如果性能可能会成为问题,那么请参阅由@Matteo运行的测试 – Colin

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; 
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(); 
3

你可以这样做用简单的linq

int num = 0; 
var ints = (from str in strings 
      where int.TryParse(str,out num) 
      select num).ToList(); 
+3

这太神奇了,有点可怕。 – Rawling

+0

它在并行使用时会产生麻烦。我不会这样做。 – JohnB

+0

是什么造成麻烦? – Colin

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.TryParseInt32Converter大约快165倍。

[更新2]

Int32Converter类继承的TypeConverter.IsValid包含bug固定在.NET 4.0。

+0

快多少? foreach如何比较?并行使用避免了麻烦吗? – Colin

+0

它快了165倍。 我没有尝试,但你可以在[CodePlex]上找到我的代码片段(http://linqpadsamples.codeplex.com/wikipage?title=Benchmark%20class%20for%20performance%20tests)并修改它。 –

+0

我抓住了LINQPad并尝试了这段代码。但是我得到“输入字符串格式不正确”。来自int.Parse。如果我将所有字符串更改为整数,则速度提高166%.... – Colin