47

LINQ の構文について少しだけ。でフラット化しIEnumerable<IEnumerable<T>>ていSelectMany(x => x)ます。

私の問題はラムダ式にありx => xます。ちょっと見栄えが悪いです。代わりに使用できる静的な「アイデンティティ関数」オブジェクトはありx => xますか? みたいなSelectMany(IdentityFunction)

4

7 に答える 7

37

質問を誤解していない限り、C# 4 では次のようにするとうまくいくようです。

public static class Defines
{
   public static T Identity<T>(T pValue)
   {
      return pValue;
   }

   ...

次に、例で次のことを実行できます。

var result =
   enumerableOfEnumerables
   .SelectMany(Defines.Identity);

Defines.Identityのようなラムダを使用する場所ならどこでも使用できますx => x

于 2012-06-05T08:00:51.500 に答える
31

注: この回答は C# 3 では正しかったのですが、ある時点 (C# 4? C# 5?) で型推論が改善され、IdentityFunction以下に示す方法が簡単に使用できるようになりました。


いいえ、ありません。まず、一般的である必要があります。

public static Func<T, T> IdentityFunction<T>()
{
    return x => x;
}

ただし、型推論は機能しないため、次のようにする必要があります。

SelectMany(Helpers.IdentityFunction<Foo>())

これは よりもずっと醜いですx => x

別の可能性は、これを拡張メソッドでラップすることです。

public static IEnumerable<T> Flatten<T>
    (this IEnumerable<IEnumerable<T>> source)
{
    return source.SelectMany(x => x);
}

残念ながら、一般的な差異がそのままでは、C# 3 のさまざまなケースに該当する可能性がありますList<List<string>>。たとえば、これは適用できません。より一般的にすることができます:

public static IEnumerable<TElement> Flatten<TElement, TWrapper>
    (this IEnumerable<TWrapper> source) where TWrapper : IEnumerable<TElement>
{
    return source.SelectMany(x => x);
}

しかし、繰り返しになりますが、型推論の問題があると思います...

編集:コメントに返信するには...はい、C# 4 ではこれが簡単になります。というか、最初のFlattenメソッドが C# 3 よりも便利になります。次の例は、C# 4 では機能しますが、C# 3 では機能しません。これは、コンパイラが からList<List<string>>に変換できないためIEnumerable<IEnumerable<string>>です。

using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensions
{
    public static IEnumerable<T> Flatten<T>
        (this IEnumerable<IEnumerable<T>> source)
    {
        return source.SelectMany(x => x);
    }
}

class Test
{
    static void Main()
    {
        List<List<string>> strings = new List<List<string>>
        {
            new List<string> { "x", "y", "z" },
            new List<string> { "0", "1", "2" }
        };

        foreach (string x in strings.Flatten())
        {
            Console.WriteLine(x);
        }
    }
}
于 2009-09-23T15:23:43.273 に答える
6

C# 6.0 では状況が改善されています。@Sahuagin が提案する方法で恒等関数を定義できます。

static class Functions
{
    public static T It<T>(T item) => item;
}

そしてSelectManyusing staticコンストラクターで使用します。

using Functions;

...

var result = enumerableOfEnumerables.SelectMany(It);

そういう意味では非常に簡潔に見えると思います。また、辞書を作成するときに識別機能が役立つこともわかりました。

class P
{
    P(int id, string name) // Sad. We are not getting primary constructors in C# 6.0
    {
        ID = id;
        Name = id;
    }

    int ID { get; }
    int Name { get; }

    static void Main(string[] args)
    {
        var items = new[] { new P(1, "Jack"), new P(2, "Jill"), new P(3, "Peter") };
        var dict = items.ToDictionary(x => x.ID, It);
    }
}
于 2015-01-20T09:27:43.717 に答える
3

これはあなたが望むように働くかもしれません。Jonがこのソリューションのバージョンを投稿したことはわかりましたが、彼には2番目の型パラメーターがあります。これは、結果のシーケンスタイプがソースシーケンスタイプと異なる場合にのみ必要です。

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source)
    where T : IEnumerable<T>
{
    return source.SelectMany(item => item);
}
于 2009-09-23T23:31:12.943 に答える
2

必要なものに近づけることができます。通常の静的関数の代わりに、アイデンティティ関数が型ではなくコレクションのものであるかのように、 の拡張メソッドを検討してくださいIEnumerable<T>(コレクションはアイテムのアイデンティティ関数を生成できます)。

public static Func<T, T> IdentityFunction<T>(this IEnumerable<T> enumerable)
{
     return x => x;
}

これにより、タイプを再度指定する必要がなくなり、次のように記述します。

IEnumerable<IEnumerable<T>> deepList = ... ;
var flat = deepList.SelectMany(deepList.IdentityFunction());

ただし、これは少し乱暴に感じますが、おそらくx=>x. また、 (チェーンで)流暢に使用できないため、常に役立つとは限りません。

于 2009-09-23T17:00:49.323 に答える