LINQ身份功能?

时间:2009-09-23 15:20:36

标签: c# linq lambda linq-to-objects

关于LINQ语法的一点点关注。我正在使用IEnumerable<IEnumerable<T>>展平SelectMany(x => x)

我的问题在于lambda表达式x => x。它看起来有点难看。是否有一些静态的“身份功能”对象,我可以使用而不是x => x?像SelectMany(IdentityFunction)

这样的东西

7 个答案:

答案 0 :(得分:30)

注意:这个答案对于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中不起作用,因为编译器无法从{{1转到List<List<string>>

IEnumerable<IEnumerable<string>>

答案 1 :(得分:26)

除非我误解了这个问题,否则以下内容在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的lambda。

答案 2 :(得分:7)

使用C#6.0,如果你引用FSharp.Core,你可以这样做:

using static Microsoft.FSharp.Core.Operators

然后你可以自由地做:

SelectMany(Identity)

答案 3 :(得分:3)

这是否按您想要的方式工作?我意识到Jon发布了这个解决方案的一个版本,但他有第二个类型参数,只有在结果序列类型与源序列类型不同时才需要。

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

答案 4 :(得分: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。此外,你不能流利地使用它(链接),因此它并不总是有用。

答案 5 :(得分:2)

使用C#6.0,情况正在好转。我们可以按@Sahuagin建议的方式定义Identity函数:

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

然后在SelectMany using static构造函数中使用它:

using Functions;

...

var result = enumerableOfEnumerables.SelectMany(It);

我认为这样看起来非常简洁。我还发现在构建字典时Identity功能很有用:

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

答案 6 :(得分:1)

我选择一个带有单个静态属性的简单类,并在行下添加所需数量的

    internal class IdentityFunction<TSource>
    {
        public static Func<TSource, TSource> Instance
        {
            get { return x => x; }
        }
    }

    SelectMany(IdentityFunction<Foo>.Instance)