【发布时间】:2010-09-24 07:04:31
【问题描述】:
我想在 C# 中生成一个列表。我缺少 python 的列表理解。有没有像 Python 中的列表推导或生成器表达式那样动态创建集合的 C# 方法?
【问题讨论】:
标签: c# python list list-comprehension
我想在 C# 中生成一个列表。我缺少 python 的列表理解。有没有像 Python 中的列表推导或生成器表达式那样动态创建集合的 C# 方法?
【问题讨论】:
标签: c# python list list-comprehension
如果您使用的是 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();
【讨论】:
Matt 提到了查询表达式。顺便说一下,这些通常可用于 LINQ - 而不仅仅是 LINQ to Objects。 (例如,应用于 LINQ to SQL 数据上下文的相同查询将在数据库上执行过滤器和投影。)
C# 3 中的查询表达式只是编写普通 C# 代码的语法糖——尽管查询表达式通常最终调用extension methods。 (它们不必这样做,编译器也不关心,但它们通常会这样做。)您可以对集合执行各种操作,这些操作在 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());
如果您想(比如说)根据原始集合中值的索引进行过滤,您可以使用appropriate overload of 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# 以 iterator blocks 的形式提供了它。事实上,这些在实现类似 LINQ 的运算符时非常有用。 (因为大多数 LINQ to Objects 都基于扩展方法,您可以将自己的运算符添加到 LINQ 中,这些运算符看起来“本机” - 尽管您不能自己更改查询表达式语法。)
【讨论】:
Enumerable.Range 作为“来源”,而不是你的答案中的 for 循环。
Enumerable.Range 指针。除此之外,该 C# 代码是否等同于 python 的列表理解?或者,在 C# 中有没有更优雅的方式?与 python 的单行代码相比,C# 代码看起来很庞大。
string.Join(", ", Enumerable.Range(1, 50).Select(x => $"cb{x:00}"))),真的没那么长……
$"cb{x:00}" 更改为string.Format("cb{0:00}", x),所以并没有非常 太大的区别 - 但是是的,它仍然是一个改进。
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
}
}
(注意:使用Array.ConvertAll的数组也可以这样做
【讨论】:
我知道这是一个非常晚的答案,但我也想知道 C# 是否有任何相当于 python 的列表理解的东西。据我所知,Matt Campbell 和 Jon Skeet 的答案显示了如何提取现有列表中的列表。但是 python 的列表推导也可以从头开始创建列表。所以这就是我想出的。
首先我将展示 Python 列表推导式,然后展示我想出的 C# 等价物。
玩具任务是创建一个这样的字符串
cb01, cb02, cb02, ... , cb50
s = ', '.join('cb{0:02}'.format(i+1) for i in range(50))
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 feature。如果你还没有使用 C# 6,你可以使用旧的 String.Format() 方式。
【讨论】:
有这个:
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()
【讨论】: