views:

212

answers:

7
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);

If you loop through b it will now only contain d e and f. Can anyone expand out whats actually happening, because currently it doesnt make any sense at all.

Edit: I was more talking about the use of predicates. How does know how to pass what into where?

+5  A: 
b.RemoveAll(<function that takes a string and returns true if we want to remove it>)

no lambda expression required.

perhaps you'd like for the line to read

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

however, the function x=> a.Contains(x) is simply a function that takes a string and returns a bool indicating whether a contains x. a.Contains is already a function that does that.

Jimmy
+2  A: 

It says 'remove all elements in b that are contained in a'. So you're left only with the one's in b that weren't also present in a.

Tony
+3  A: 

look at it like this:

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

You passing the bit in the if evaluation clause as a delegate (managed equivalent of a function pointer). The RemoveAll method unrolls the list and does the rest.

dkackman
The lambda part is wrong. You are really passing a delegate.
ChaosPandion
good point. will edit
dkackman
Lambdas are those things with the lambda operator, aka strange arrow: `=>`. `a.Contains` is a method group, and the compiler automagically creates a delegate for it that is of the type expected by RemoveAll (`Predicate<string>`).
Martinho Fernandes
A: 

For every element in b, a.Contains() is being evaluated for that element. If it's true, it is removed.

So, you're removing every element from b that is also contained in a.

The function "a.Contains" is being passed as an argument to RemoveAll.

Sapph
+1  A: 

Here is a slightly expanded version of your code that shows what's happening:

 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);
Tarydon
+5  A: 

The { } Syntax is a collection initializer. The code is equivalent to

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 is a function that calls another function and passes in a string. It's like this:

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

a.Contains is a function that takes a string and returns a bool. So the code can be changed to:

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

Note that in this Lambda-Syntax, you are passing the "a.Contains" function - not the result of the function! It's like a function pointer. RemoveAll expects to take a function in the form of "bool FunctionName(string input)".

Edit: Do you know what delegates are? They are a bit like function pointers: A delegate specifies a signature ("Takes 2 strings, returns an int"), and then you can use it like a variable. If you don't know about delegates, read Karl Seguins article.

Some delegates are needed extremely often, so the .net Framework developers added three types of extremely common delegates:

  • Predicate: A delegate that takes a T and returns a bool.
  • Action: A delegate that takes 1 to 4 parameters and returns void
  • Function: A delegate that takes 0 to 4 parameters and returns a T

(Shamelessly copied from Jon Skeet's Answer here)

So predicate is just the name given for a delegate, so that you don't have to specify it yourself.

If you have ANY function in your assembly with the signature

"bool YourFunction(string something)", it is a Predicate<string> and can be passed into any other function that takes one:

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);
}

The whole point is separation of concerns (see the comment in SomeGenericFunction) and also to have very generic functions. Look at my generic, extensible string encoding function. This uses the Func rather than the Predicate delegate, but the purpose is the same.

Michael Stum
A: 

The signature for RemoveAll looks like this ...

public int RemoveAll(Predicate<T> match);

A Predicate<T> is a delegate that takes Ts and returns bools ...

public delegate bool Predicate<T>(T obj)

RemoveAll is thus asking for a reference to a method that will, in your case, take strings and return bools. List<T>.Contains is such a method. You will note that the signature for List<T>.Contains matches the Predicate delegate (it takes Ts and returns bools) ...

public bool Contains(T item);

RemoveAll will apply whatever Predicate is passed as "match" to each element of the list upon which it is called (b in your case). So, if a.Contains("a"), for example, returns true, then all the a's will be removed from the b list. Thus, in your example, all the a's, b's and c's are removed.

Using a tool like .NET Reflector will let you look at the code of RemoveAll and that might help clarify what's happening under the covers.

JP Alioto