2012-05-28 51 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