Есть ли в C # что-нибудь сравнимое со списком Python? - PullRequest
19 голосов
/ 27 ноября 2008

Я хочу создать список в C #. Мне не хватает понимания списка Python. Есть ли в C # способ создавать коллекции на лету, как в списках или в выражениях-генераторах?

Ответы [ 5 ]

22 голосов
/ 27 ноября 2008

Если вы используете 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();
15 голосов
/ 27 ноября 2008

Мэтт упомянул выражения запроса. Кстати, они доступны для LINQ вообще, а не только для LINQ to Objects. (Например, тот же запрос, примененный к текстовому тексту LINQ to SQL, будет выполнять фильтрацию и проекцию базы данных.)

Выражения запроса в C # 3 являются просто синтаксическим сахаром над написанием нормального кода C # - хотя выражения запроса обычно заканчиваются вызовом методов расширения . (Они не обязаны это делать, и компилятору это не важно, но обычно это так.) Существуют различные вещи, которые вы можете делать с коллекциями, которые недоступны в выражениях запросов C #, но которые поддерживаются вызовами методов, поэтому Стоит знать о обоих видах синтаксиса. Например, выражение запроса Мэтта:

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 - хотя вы не можете изменить синтаксис выражения запроса самостоятельно.)

5 голосов
/ 29 апреля 2010

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 вы можете передать лямбда-функцию, указывающую, какой тип функции отображения необходим.

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

1 голос
/ 30 июня 2015

Вот это:

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()
0 голосов
/ 23 февраля 2017

Я знаю, что это очень очень поздний ответ, но я тоже удивлялся, есть ли в C # что-нибудь, эквивалентное пониманию списка Python. Ответы от Мэтта Кэмпбелла и Джона Скита показывают, как извлечь список из другого существующего, насколько я понял. Но понимание списка 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());

Я не уверен, что переусердствовал или нет.


Редактировать: (из того, что Джон Скит упомянул в своем комментарии, настоящий однострочный, эквивалентный пониманию списка Python)

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

Обратите внимание, что $ - это C # 6 функция . Если вы все еще не используете C # 6, вы можете пойти по старому пути String.Format().

...