Закрытия и лямбда в C # - PullRequest
       15

Закрытия и лямбда в C #

11 голосов
/ 20 августа 2009

Я получаю основные принципы замыканий и лямбда-выражений, но я пытаюсь сосредоточиться на том, что происходит за кулисами, и когда это / нецелесообразно использовать в моем коде. Рассмотрим следующий пример, который принимает коллекцию имен и возвращает любые имена, начинающиеся с буквы C ...

    static void Main(string[] args)
    {
        List<string> names = new List<string>();
        names.AddRange(new string[]
        {
            "Alan", "Bob", "Chris", "Dave", "Edgar", "Frank"
        });


        names.FindAll(x => x.StartsWith("C")).ForEach(
            i => Console.WriteLine(i));

    }

Во-первых, есть ли более прямой способ, которым я мог бы написать это выражение? Во-вторых, не собирается ли «FindAll» выделить память для новой коллекции, которая содержит соответствующие элементы? Я определенно вижу, что синтаксис более элегантный, но я хочу убедиться, что не буду вдаваться в проблемы с производительностью позже при работе с большими коллекциями. Компилятор делает некоторую оптимизацию вуду за кулисами, которая делает мои опасения недействительными?

Ответы [ 5 ]

15 голосов
/ 20 августа 2009

Да, FindAll создаст новый список. Вы хотите «Где», которое будет возвращать объект IEnumerable, который знает, как перебрать существующий список:

foreach (string name in names.Where(n => n.StartsWith("C") ) ) 
{
    Console.WriteLine(name);
}

Но в этом коде нет замыкания, потому что нет локальной переменной для захвата.

12 голосов
/ 20 августа 2009

Другие ответы, которые говорят, чтобы использовать «Где», являются правильными. Дополнительный момент: вы также можете использовать синтаксис понимания запросов, чтобы «Где» выглядело лучше:

   var query = from name in names where name.StartsWith("C") select name;
   foreach(var result in query) Console.WriteLine(result);

Обратите внимание, что в качестве стилистической задачи я рекомендую, чтобы выражения не имели побочных эффектов, а операторы всегда имели побочные эффекты. Поэтому я лично использовал бы выражение foreach , а не подвыражение ForEach для выполнения побочного эффекта вывода. Многие люди не согласны с этим, но я думаю, что это делает код более понятным.

2 голосов
/ 20 августа 2009

Вы правы, что использование метода List<T>.FindAll создаст и вернет новый List<T>.

Если вы можете использовать LINQ, существует множество методов, которые передают результаты по одному элементу за раз, а не возвращают полностью заполненную коллекцию:

foreach (var i in names.Where(x => x.StartsWith("C")))
{
    Console.WriteLine(i);
}

Нет встроенного ForEach метода, который действует на IEnumerable<T>, но написать свое собственное расширение просто, если вам действительно нужна эта функциональность:

names.Where(x => x.StartsWith("C")).ForEach(Console.WriteLine);

// ...

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (T item in source)
    {
        action(item);
    }
}
2 голосов
/ 20 августа 2009

Вы должны использовать Where вместо FindAll. Where будет выполнять итерацию по коллекции для вашего условия и позволит вам выполнить действие, а не создавать новую коллекцию, соответствующую вашему условию, а затем выполнять итерацию по TH и выполнять ваше действие.

0 голосов
/ 20 августа 2009

Что делает выражение конкретно замыканием является лексическим ограничением, нет?

string prefix = "C";  
// value of prefix included in scope  
names.FindAll(x => x.StartsWith(prefix)).ForEach(...);    

или даже

Func filter = null;

{
    string prefix = "C";
    // value of prefix included in scope
    filter = x => x.StartsWith (prefix);    
}

// find all names starting with "C"
names.FindAll (filter).ForEach (...);       

Или я что-то упускаю или делаю необоснованные предположения?

...