2010-02-02 31 views
4

我想解釋一下我的問題的最佳方法是使用短(一般)LINQ到對象的代碼示例:C#Linq/Lambda表達式:如何從字符串中選擇一個整數?

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)); 
} 

注意,這個代碼解析整數的兩倍。我知道我錯過了一個顯而易見的簡單方法來安全地消除這些電話中的一個(也就是我以前做過的)。我現在找不到它。我怎樣才能做到這一點?

回答

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被定義爲轉換失敗)之外,我沒有看到許多替代方案,其中兩者都不太好。

+0

我看着這種方法。我只是不喜歡編譯器無法推斷出類型(至少在C#3中)的事實,所以「Reduce」擴展可用性受損...... –

+0

我主要的抱怨是** 1)**,我可以不會在單一陳述中表達轉換。在lambda中我仍然需要一個變量聲明。和** 2)**,我必須將結果表示爲一個元組而不是轉換後的項目。 –

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)。

+0

我想我正在尋找更多的基於複雜轉換過濾枚舉的通用情況 - 保留通過更改的所有內容的更改版本。編寫一個新的「運營商」可能是這種情況。 –

+0

使用'!= null'和'GetValueOrDefault()'真的比使用'where parsed.HasValue'和'select parsed.Value'更快嗎?我想我應該去做一些測試,因爲這對我來說似乎是反直覺的。 –

+0

另一種方法是編寫一個方法,返回一個'Tuple '結果,如果你有.NET 4或者想編寫自己的Tuple類。這就是F#自動處理TryParse和類似方法的方式。然後LINQ將'在哪裏tuple.Item1選擇tuple.Item2' –

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

當然,這是大多隻是推動工作納入拉姆達,但它確實提供正確的答案,用一個單一的解析(但額外的內存分配)。

+0

馬克的選擇使用可爲空可以在這裏使用,而不是匿名類,這也可以防止發生GC壓力... –

相關問題