LINQ身份功能?

Joe*_*Joe 39 c# linq lambda linq-to-objects

关于LINQ语法只是一点点关注.我压扁的IEnumerable<IEnumerable<T>>SelectMany(x => x).

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

Dav*_*eau 32

除非我误解了这个问题,否则以下内容在C#4中似乎对我有用:

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

   ...
Run Code Online (Sandbox Code Playgroud)

然后,您可以在示例中执行以下操作:

var result =
   enumerableOfEnumerables
   .SelectMany(Defines.Identity);
Run Code Online (Sandbox Code Playgroud)

除了在Defines.Identity任何地方使用,你都可以使用看起来像的lambda x => x.

  • 我知道这是一个迟交,但我真的觉得它更好地回答了这个问题. (4认同)

Jon*_*eet 30

注意:这个答案对于C#3是正确的,但在某些时候(C#4?C#5?)类型推断得到了改进,因此可以很容易地使用IdentityFunction下面所示的方法.


不,没有.它必须是通用的,首先是:

public static Func<T, T> IdentityFunction<T>()
{
    return x => x;
}
Run Code Online (Sandbox Code Playgroud)

但是类型推断不起作用,所以你必须这样做:

SelectMany(Helpers.IdentityFunction<Foo>())
Run Code Online (Sandbox Code Playgroud)

这比很丑陋x => x.

另一种可能性是你在扩展方法中包装它:

public static IEnumerable<T> Flatten<T>
    (this IEnumerable<IEnumerable<T>> source)
{
    return source.SelectMany(x => x);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,随着它的通用方差,它可能会在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);
}
Run Code Online (Sandbox Code Playgroud)

但是,我再次提到了类型推断问题,我怀疑......

编辑:回应评论......是的,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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么你写一个函数返回一个函数?不应该只需要一个带有`Func <T,T>`签名的单一函数,除了返回它的输入之外什么都不做? (3认同)
  • @JonSkeet我的意思是身份功能.你写了`public static Func <T,T> IdentityFunction <T>()`,它不是一个身份函数,它是一个返回一个身份函数的函数.在C#3中有必要吗?我添加了我的版本作为答案. (3认同)
  • 谢谢!这给了我一些有趣的线索.我想我暂时待在x => x ... (2认同)

jbt*_*ule 9

使用C#6.0并且如果您引用FSharp.Core,您可以:

using static Microsoft.FSharp.Core.Operators
Run Code Online (Sandbox Code Playgroud)

然后你可以自由地做:

SelectMany(Identity)
Run Code Online (Sandbox Code Playgroud)

  • FSharp.Core是一个很大的依赖项。除非您实际上正在编写F#,否则可能不值得 (4认同)

ie.*_*ie. 5

使用 C# 6.0 事情变得更好了。我们可以按照@Sahuagin 建议的方式定义 Identity 函数:

static class Functions
{
    public static T It<T>(T item) => item;
}
Run Code Online (Sandbox Code Playgroud)

然后在使用它SelectManyusing static构造函数:

using Functions;

...

var result = enumerableOfEnumerables.SelectMany(It);
Run Code Online (Sandbox Code Playgroud)

我认为这样看起来非常简洁。我还发现 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!在您的代码示例中,您忘记在“使用静态函数”中写入 *static* (2认同)