2009-09-23 16 views
30

Sadece LINQ sözdizimi hakkında küçük bir kıkırdak. IEnumerable<IEnumerable<T>>'u SelectMany(x => x) ile düzleştiriyorum.LINQ kimlik fonksiyonu?

Sorunum, x => x numaralı lambda ifadesidir. Biraz çirkin görünüyor. x => x yerine kullanabileceğim bazı statik 'kimlik işlevi' nesnesi var mı? SelectMany(IdentityFunction) gibi bir şey mi?

+6

Neden SKI birleştiricilerini C# içine dahil etmediklerini asla anlayamayacağım. –

cevap

26

Not: Bu yanıt, C# 3 için doğru, ama bir noktada (C# 4 C 5.?) En aşağıda gösterilen IdentityFunction yöntem rahatlıkla kullanılabilir, böylece sonuç gelişmiş yazın.

( ) Tek yapmanız gerekirdi bu yüzden, işe yaramaz çıkarım yazın ardından

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

Ama: Bu ilk başta, jenerik olması gerekir

x => x daha çok çirkin bir
SelectMany(Helpers.IdentityFunction<Foo>()) 

.

başka olasılık bir uzantı yönteminde sar şudur:

Maalesef jenerik varyans iyi C# 3 değişik vakaların faul düşebilir bu şekilde, birlikte
public static IEnumerable<T> Flatten<T> 
    (this IEnumerable<IEnumerable<T>> source) 
{ 
    return source.SelectMany(x => x); 
} 

... değil mi Örneğin, List<List<string>>'a uygulanabilir.

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

Ama yine, daha sonra tür kesmesi sorunlarımız var, sanıyorum ...

EDIT: Bunu daha genel yapabiliriz yorumlarla ... evet, C# 4 markaları yanıt vermek için bu daha kolay. Ya da, ilk Flatten yöntemini C# 3'tekinden daha kullanışlı hale getirir. Burada C# 4'te çalışan bir örnek var, ancak derleyici List<List<string>>'dan 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); 
     } 
    } 
} 
+2

Teşekkürler! Bu bana da bazı ilginç ipuçları verdi. Ben şu anda x => x ile kalacağım düşünüyorum ... – Joe

+0

Ben de bir Flatten uzantısı yöntemi oluşturmaya çalıştım (hangi SelectMany denir ama hedef aynıydı) ve varyans sorunu nedeniyle o wasn ' t gerçekten faydalı, çok fazla aşırı yüklenmeye ihtiyacım var, bu yüzden x => x ... Jon'a geri döndüm, Flatten bir uzatma yöntemi C# 4/VB10'da daha kolay mı olur? –

+0

@MetaKnight: Yazıları düzenle ... –

1

İhtiyacınız olan şeylere yakın alabilirsiniz. kimlik işlevi toplama, sanki yerine düzenli bir statik işlev, sizin IEnumerable<T> için bir uzantısı Yöntemi düşünün değil tipi (bir koleksiyon öğeleri için kimlik işlevi üretebilir): Bununla

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

tekrar türünü belirtmek zorunda ve yazmayın:

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

gerçi küfürlü biraz duygu ve muhtemelen x=>x ile gitmek istiyorum. Ayrıca, akıcı bir şekilde (zincirleme halinde) kullanamazsınız, bu yüzden her zaman kullanışlı olmayacaktır.

+0

'SelectMany' ile çalışmasını sağlamak için' Func > '' i döndürmeniz gerekir. –

+1

Doğru, "Seç" için çalışır, ancak 'SelectMany' için başarısız - türleri anlayamaz. utanç. – Kobi

3

Bu, istediğiniz gibi çalışır mı? Jon'un bu çözümün bir versiyonunu yayınladığının farkındayım, ancak o ancak sonuçta elde edilen dizi tipi kaynak dizi tipinden farklıysa gerekli olan ikinci tip bir parametreye sahip.

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

As:

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

    ... 

Ardından örnekte aşağıdakileri yapabilirsiniz: Soruyu yanlış anlamak sürece

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source) 
    where T : IEnumerable<T> 
{ 
    return source.SelectMany(item => item); 
} 
24

aşağıdaki C# 4'te benim için iyi iş gibi görünüyor Defines.Identity'u kullanacağınız her yerde x => x gibi görünen bir lambda kullanabilirsiniz.

+3

Bunun geç teslim olduğunu biliyorum, ama bu soruya daha iyi cevap vermiş gibi hissediyorum. – Anthony

1

C# 6.0 ile işler daha iyi hale geliyor.

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

ve sonra SelectMany yılında using static yapıcı kullanmak: Biz @Sahuagin önerdiği şekilde Kimlik fonksiyonunu tanımlayabiliriz Ben böyle bir şekilde çok kısa ve öz görünüyor düşünüyorum

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

C# 6.0 ile ve FSharp.Core başvurursanız yapabileceğiniz: sözlükleri oluştururken ben de yararlı Kimlik fonksiyonunu bulmak sonra

using static Microsoft.FSharp.Core.Operators 

Ve senin yapmakta özgür:

SelectMany(Identity) 
0

Tek bir statik özellik ile basit bir sınıfla gider ve

satırından gerektiği kadar ekleyin