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

呸!我找不到将转换和过滤结合在一个呼叫中的方法,最终得到的结果似乎比原来的要糟糕得多。有没有办法做到这一点,还是我应该坚持我的 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错误。

4

5 回答 5

3

你可以用一个简单的 linq 做到这一点

int num = 0;
var ints = (from str in strings
            where int.TryParse(str,out num)
            select num).ToList();
于 2012-05-28T14:25:11.327 回答
3

int使用LINQ的另一种方法是:

var integerList =
    strings
        .Where(x => new Int32Converter().IsValid(x))
        .Select(x => int.Parse(x));

[更新]

我使用 LINQPad进行了性能测试。

这里我报告代码和结果:

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快约 165 倍Int32Converter

[更新 2]

TypeConverter.IsValid继承的Int32Converter包含一个在 .NET 4.0 中修复的错误。

于 2012-05-28T14:34:19.590 回答
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;
于 2012-05-28T14:04:19.503 回答
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();
于 2012-05-28T14:15:40.263 回答
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 ();
于 2012-05-28T14:33:06.293 回答