只是LINQ语法有点挑剔。 我压扁的IEnumerable<IEnumerable<T>>
用SelectMany(x => x)
我的问题是与lambda表达式x => x
。 它看起来有点丑。 有一些静态的“身份功能”的对象,我可以使用,而不是x => x
? 类似SelectMany(IdentityFunction)
只是LINQ语法有点挑剔。 我压扁的IEnumerable<IEnumerable<T>>
用SelectMany(x => x)
我的问题是与lambda表达式x => x
。 它看起来有点丑。 有一些静态的“身份功能”的对象,我可以使用,而不是x => x
? 类似SelectMany(IdentityFunction)
注意:此答案是正确的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);
}
}
}
除非我误解了问题,下面似乎在C#4至正常工作对我来说:
public static class Defines
{
public static T Identity<T>(T pValue)
{
return pValue;
}
...
那么你可以做你的榜样如下:
var result =
enumerableOfEnumerables
.SelectMany(Defines.Identity);
除了使用Defines.Identity
任何地方,你会使用lambda看起来像x => x
。
在C#6.0,如果引用FSharp.Core你可以这样做:
using static Microsoft.FSharp.Core.Operators
然后,你可以自由地做:
SelectMany(Identity)
请问在您希望的方式工作的呢? 我知道乔恩发布了版本的解决方案,但他有一个第二类参数,只需要如果产生的序列类型与源序列类型不同。
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source)
where T : IEnumerable<T>
{
return source.SelectMany(item => item);
}
你可以接近你所需要的。 而不是常规的静态函数,可以考虑扩展方法为您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
。 此外,您还不能流利地使用它(以链接),所以它不会总是有用的。
用C#6.0的东西越来越好。 我们可以定义由@Sahuagin建议的方式确定身份的功能:
static class Functions
{
public static T It<T>(T item) => item;
}
然后在使用它SelectMany
的using 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);
}
}
我会去用一个简单的类单一静态属性,并根据需要上下行添加尽可能多
internal class IdentityFunction<TSource>
{
public static Func<TSource, TSource> Instance
{
get { return x => x; }
}
}
SelectMany(IdentityFunction<Foo>.Instance)