22

我想在 C# 中生成一个列表。我缺少 python 的列表理解。是否有一种 C# 方法可以像 Python 中的列表推导或生成器表达式那样动态创建集合?

4

5 回答 5

28

如果您使用的是 C# 3.0 (VS2008),那么 LINQ to Objects 可以做非常相似的事情:

List<Foo> fooList = new List<Foo>();
IEnumerable<Foo> extract = from foo in fooList where foo.Bar > 10 select Foo.Name.ToUpper();
于 2008-11-27T05:59:36.347 回答
15

Matt 提到了查询表达式。顺便说一下,这些通常可用于 LINQ - 而不仅仅是 LINQ to Objects。(例如,应用于 LINQ to SQL 数据上下文的相同查询将在数据库上执行过滤器和投影。)

C# 3 中的查询表达式只是编写普通 C# 代码的语法糖——尽管查询表达式通常最终调用扩展方法。(它们不必这样做,编译器也不关心,但它们通常会这样做。)您可以对集合执行各种操作,这些操作在 C# 查询表达式中不可用,但受方法调用支持,因此值得了解这两种语法。例如,Matt 的查询表达式为:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = from foo in fooList where foo.Bar > 10 select foo.Name.ToUpper();

被“预处理”成:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = fooList.Where(foo => foo.Bar > 10)
                                     .Select(foo => foo.Name.ToUpper());

如果您想(比如说)根据原始集合中值的索引进行过滤,您可以使用Where 的适当重载,该重载通过查询表达式不可用:

List<Foo> fooList = new List<Foo>();
IEnumerable<string> extract = fooList.Where((foo, index) => foo.Bar > 10 + index)
                                     .Select(foo => foo.Name.ToUpper());

或者您可以找到符合条件的最长名称的长度:

List<Foo> fooList = new List<Foo>();
int longestName = fooList.Where((foo, index) => foo.Bar > 10 + index)
                         .Select(foo => foo.Name)
                         .Max();

(您不必在单独的方法中进行投影和最大值 - 有一个重载Max也需要一个投影。)

我的观点是,使用扩展方法,您可以非常轻松地构建复杂的查询。

您还提到了 Python 生成器 - C# 以迭代器块的形式具有此功能。事实上,这些在实现类似 LINQ 的运算符时非常有用。(因为大多数 LINQ to Objects 都基于扩展方法,所以您可以将自己的运算符添加到 LINQ 中,这些运算符看起来“本机” - 尽管您自己不能更改查询表达式语法。)

于 2008-11-27T06:24:33.317 回答
7

List<T>.ConvertAll通过对现有列表中的每个项目执行相同的操作,然后返回一个新集合,其行为就像列表推导式一样。这是使用 Linq 的替代方法,尤其是在您仍在使用 .NET 2.0 时。

在 Python 中,一个简单的列表理解示例:

>>> foo = [1, 2, 3]
>>> bar = [x * 2 for x in foo]
>>> bar
[2, 4, 6]

对于 C# 3.0,您可以传递一个 lambda 函数来指定所需的映射函数类型。

public static void Main()
{
    var foo = new List<int>{ 1, 2, 3};
    var bar = foo.ConvertAll(x => x * 2);    // list comprehension

    foreach (var x in bar)
    {
        Console.WriteLine(x);  // should print 2 4 6
    }
}

对于 C# 2.0,您可以使用带有Converter委托的匿名方法来执行等效操作。

public static void Main()
{
    List<int> foo = new List<int>(new int[]{ 1, 2, 3});
    List<int> bar = foo.ConvertAll(new Converter<int, int>(delegate(int x){ return x * 2; }));  // list comprehension

    foreach (int x in bar)
    {
        Console.WriteLine(x);  // should print 2 4 6
    }
}

(注意:同样可以使用 Arrays 完成Array.ConvertAll

于 2010-04-29T17:25:59.427 回答
2

我知道这是一个非常晚的答案,但我也想知道 C# 是否有任何相当于 python 的列表理解的东西。据我所知, Matt CampbellJon Skeet的回答展示了如何从现有列表中提取列表。但是 python 的列表推导也可以从头开始创建列表。所以这就是我想出的。

首先,我将展示 python 列表理解,然后展示我想出的 C# 等价物。

玩具任务是创建一个这样的字符串

cb01, cb02, cb02, ... , cb50

Python列表理解:

s = ', '.join('cb{0:02}'.format(i+1) for i in range(50))

我想出的 C# 等价物:

string s = String.Join(", ", new Func<List<string>>( () =>
{
    List<string> list = new List<string>();
    foreach (int i in Enumerable.Range(1, 50))
        list.Add(String.Format("cb{0:00}", i));

    return list;
}).Invoke());

我不确定我是否做过头。


编辑:(从 Jon Skeet 在他的评论中提到的,一个真正的单行相当于 python 的列表理解)

String.Join(", ", Enumerable.Range(1, 50).Select(x => $"cb{x:00}")))

请注意,这$件事是C# 6 的功能。如果您仍然不使用 C# 6,则可以使用旧String.Format()方法。

于 2017-02-23T07:43:18.687 回答
1

有这个:

new List<FooBar> { new Foo(), new Bar() }

它只比它的 python 等价物长一点:

[Foo(), Bar()]

然后是这样的:

public IEnumerable<FooBar> myFooBarGenerator() {
     yield return new Foo();
     yield return new Bar();
}

这相当于python:

def myFooBarGenerator():
    yield Foo()
    yield Bar()
于 2015-06-30T14:48:38.197 回答