Вопрос о нечетном лямбда-выражении - PullRequest
0 голосов
/ 07 января 2010
List<string> a = new List<string>() { "a", "b", "c" };
List<string> b = new List<string>() { "a", "b", "c", "d", "e", "f" };

b.RemoveAll(a.Contains);

Если вы проведете цикл через b, теперь он будет содержать только d e и f. Может ли кто-нибудь рассказать о том, что на самом деле происходит, потому что в настоящее время это вообще не имеет никакого смысла.

Редактировать: я больше говорил об использовании предикатов. Как узнать, как передать то, что куда?

Ответы [ 7 ]

6 голосов
/ 07 января 2010
b.RemoveAll(<function that takes a string and returns true if we want to remove it>)

лямбда-выражение не требуется.

возможно, вы хотите, чтобы строка прочитала

b.RemoveAll(x => a.Contains(x))

однако функция x=> a.Contains(x) - это просто функция, которая принимает строку и возвращает логическое значение, указывающее, содержит ли a x. a.Contains - это уже функция, которая делает это.

5 голосов
/ 07 января 2010

Синтаксис {} является инициализатором коллекции. Код эквивалентен

List<string> a = new List<string>();
a.Add("a");
a.Add("b");
a.Add("c");
List<string> b = new List<string>();
b.Add("a");
b.Add("b");
b.Add("c");
b.Add("d");
b.Add("e");
b.Add("f");

b.RemoveAll - это функция, которая вызывает другую функцию и передает строку. Это так:

foreach(string s in b) {
    if(FunctionToCall(s) == true) b.Remove(s);
}

a.Contains - это функция, которая принимает строку и возвращает bool. Таким образом, код можно изменить на:

foreach(string s in b) {
    if(a.Contains(s)) b.Remove(s);
}

Обратите внимание, что в этом лямбда-синтаксисе вы передаете функцию "a.Contains" - а не результат функции! Это как указатель на функцию. RemoveAll ожидает, что примет функцию в виде "bool FunctionName (string input)".

Редактировать: Вы знаете, что такое делегаты? Они немного похожи на указатели на функции: делегат задает сигнатуру («принимает 2 строки, возвращает int»), а затем вы можете использовать ее как переменную. Если вы не знаете о делегатах, прочитайте статью Карла Сегинса .

Некоторые делегаты нужны крайне часто, поэтому разработчики .net Framework добавили три типа чрезвычайно распространенных делегатов:

  • Predicate: делегат, который принимает T и возвращает bool.
  • Действие: делегат, который принимает от 1 до 4 параметров и возвращает void
  • Функция: делегат, который принимает от 0 до 4 параметров и возвращает T

(Бесстыдно скопировано с Джона Скита Ответ здесь )

Итак, предикат - это просто имя, данное делегату, так что вам не нужно указывать его самостоятельно.

Если в вашей сборке есть ЛЮБАЯ функция с подписью

"bool YourFunction (строка чего-либо)", это Predicate<string> и может быть передано в любую другую функцию, которая принимает одну:

public bool SomeFunctionUsedAsPredicate(string someInput)
{
    // Perform some very specific functionality, i.e. calling a web
    // service to look up stuff in a database and decide if someInput is good
    return true;
}

// This Function is very generic - it does not know how to check if someInput
// is good, but it knows what to do once it has found out if someInput is good or not
public string SomeVeryGenericFunction(string someInput, Predicate<string> someDelegate)
{
    if (someDelegate.Invoke(someInput) == true)
    {
        return "Yup, that's true!";
    }
    else
    {
        return "Nope, that was false!";
    }
}

public void YourCallingFunction()
{
    string result = SomeVeryGenericFunction("bla", SomeFunctionUsedAsPredicate);
}

Весь смысл в разделении интересов (см. Комментарий в SomeGenericFunction), а также в том, чтобы иметь очень общие функции. Посмотрите на мою универсальную, расширяемую функцию кодирования строк . При этом используется Func, а не делегат Predicate, но цель та же.

3 голосов
/ 07 января 2010

посмотрите на это так:

foreach(string s in b)
{
  if(a.Contains(s))
     b.Remove(s);
}

Вы передаете бит в условии if в качестве делегата (управляемый эквивалент указателя функции). Метод RemoveAll разворачивает список и делает все остальное.

2 голосов
/ 07 января 2010

Там написано «удалить все элементы в b, которые содержатся в a». Так что у вас остались только те, кто в b, которых также не было в a.

1 голос
/ 07 января 2010

Вот немного расширенная версия вашего кода, которая показывает, что происходит:

 List<string> a = new List<string> () { "a", "b", "c" };
 List<string> b = new List<string> () { "a", "b", "c", "d", "e", "f" };
 Predicate<string> ps = a.Contains;
 b.RemoveAll (ps);
0 голосов
/ 07 января 2010

Подпись для RemoveAll выглядит следующим образом ...

public int RemoveAll(Predicate<T> match);

A Predicate<T> - это делегат, который принимает Ts и возвращает bools ...

public delegate bool Predicate<T>(T obj)

Таким образом, RemoveAll запрашивает ссылку на метод, который в вашем случае будет принимать строки и возвращать bools. List<T>.Contains такой метод. Вы заметите, что подпись для List<T>.Contains соответствует делегату Predicate (он принимает Ts и возвращает bools) ...

public bool Contains(T item);

RemoveAll будет применять любой предикат, передаваемый как «совпадение» к каждому элементу списка, для которого он вызывается (b в вашем случае). Так, если, например, a.Contains("a") вернет true, то все a будут удалены из списка b. Таким образом, в вашем примере все a, b и c удалены.

Использование такого инструмента, как .NET Reflector , позволит вам взглянуть на код RemoveAll, и это может помочь прояснить, что происходит под одеялом.

0 голосов
/ 07 января 2010

Для каждого элемента в b, a.Contains() оценивается для этого элемента.Если это правда, он удаляется.

Итак, вы удаляете каждый элемент из b, который также содержится в a.

Функция "a.Contains" выполняетсяпередан в качестве аргумента RemoveAll.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...