47

只是关于 LINQ 语法的一些小问题。我正在IEnumerable<IEnumerable<T>>SelectMany(x => x).

我的问题是 lambda 表达式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>>to转换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 回答
14

使用 C# 6.0,如果您引用 FSharp.Core,您可以执行以下操作:

using static Microsoft.FSharp.Core.Operators

然后你可以自由地做:

SelectMany(Identity)
于 2015-08-21T13:43:25.523 回答
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 发布了这个解决方案的一个版本,但是他有第二个类型参数,只有在结果序列类型与源序列类型不同时才需要。

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 回答
1

我会选择一个带有单个静态属性的简单类,并在下一行添加尽可能多的

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

    SelectMany(IdentityFunction<Foo>.Instance)
于 2015-12-01T19:15:24.483 回答