Закрытие и Лямбда в С#
Я получаю основные принципы замыканий и лямбда-выражений, но я пытаюсь обмануть то, что происходит за кулисами, и когда это/нецелесообразно использовать их в моем коде. Рассмотрим следующий пример, который берет набор имен и возвращает любые имена, начинающиеся с буквы 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" собирается выделить память для новой коллекции, содержащей соответствующие элементы? Я определенно вижу, что синтаксис более изящный, но я хочу убедиться, что я не буду заниматься проблемами производительности позже по дороге при работе с большими коллекциями. Компилятор делает некоторую оптимизацию voodoo за кулисами, что делает мои проблемы недействительными?
Ответы
Ответ 1
Да, FindAll создаст новый список. Вы хотите "Где", который вернет объект IEnumerable, который знает, как перебирать ваш существующий список:
foreach (string name in names.Where(n => n.StartsWith("C") ) )
{
Console.WriteLine(name);
}
Но в этом коде нет замыкания, потому что нет локальной переменной для захвата.
Ответ 2
Другие ответы, которые говорят, чтобы использовать "Где", являются правильными. Дополнительная точка: вы также можете использовать синтаксис понимания запроса, чтобы сделать "Where" более приятным:
var query = from name in names where name.StartsWith("C") select name;
foreach(var result in query) Console.WriteLine(result);
Обратите внимание, что в качестве стилистической проблемы я рекомендую, чтобы выражения не имели побочных эффектов, а утверждения всегда имели побочные эффекты. Поэтому я лично использовал бы выражение foreach, а не подвыражение ForEach для выполнения выходного побочного эффекта. Многие люди не согласны с этим, но я думаю, что это делает код более понятным.
Ответ 3
Вы должны использовать Where
вместо FindAll
. Where
будет перебирать коллекцию для вашего условия и позволить вам выполнить ваше действие, а не создавать новую коллекцию, которая соответствует вашему условию, а затем повторять THAT и выполнять ваши действия.
Ответ 4
Вы правы, что использование метода 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);
}
}
Ответ 5
Что делает выражение специально закрытие лексическим охватом, no?
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 (...);
Или я что-то упускаю или делаю необоснованные предположения?