3

Linq や Lambda ステートメントを使用せずに C# を記述することに慣れていますが、理解を深めたいと考えています。foreachループを使用して C# 2.0 で記述された次のようなコードがあります。

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

うん!変換とフィルタリングを 1 回の呼び出しで組み合わせる方法を見つけることができず、元のものよりもはるかに悪いように見えるものになってしまいました。これを行う方法はありますか、それとも foreach に固執する必要がありますか?

編集:調査結果を要約すると、.NET 4.0 を使用している場合に LINQ でこれを行う 2 つの方法があります。

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

2 番目の方法は高速であり、.NET 3.5 では壊れていません。そして、foreach の代わりに、受け入れられた回答に従って、これを拡張メソッド内に配置しない理由はおそらくないでしょう。バグについては接続の問題を参照してください。Int32Converter

4

5 に答える 5

3

LINQintのみを使用して取得する別の方法は次のとおりです。

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によって継承されるには、.NET 4.0 で修正されたバグが含まれています。Int32Converter

于 2012-05-28T14:34:19.590 に答える
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 に答える
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 に答える