奇怪的Lambda表达问题

时间:2010-01-07 01:29:44

标签: c# .net linq .net-3.5 lambda

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 个答案:

答案 0 :(得分:6)

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

不需要lambda表达式。

也许你想要读一行

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

但是,函数 x=> a.Contains(x) 只是一个函数,它接受一个字符串并返回一个bool,指示是否包含x。 a.Contains已经是一个能够做到这一点的功能。

答案 1 :(得分:5)

{}语法是一个集合初始值设定项。代码等同于

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

请注意,在此Lambda-Syntax中,您传递的是“a.Contains”函数 - 而不是函数的结果!它就像一个函数指针。 RemoveAll 期望以“bool FunctionName(字符串输入)”的形式获取一个函数。

编辑:您知道代表是什么吗?它们有点像函数指针:委托指定签名(“取2个字符串,返回一个int”),然后你可以像变量一样使用它。如果您不了解代表,请阅读Karl Seguins article

非常需要一些代表,因此.net Framework开发人员添加了三种非常常见的委托:

  • 谓词:取一个T并返回一个布尔的代表。
  • 操作:占用1到4个参数并返回void
  • 的委托
  • 功能:一个代理,它接受0到4个参数并返回T

(从Jon Skeet的Answer here无耻地复制)

因此谓词只是委托给出的名称,因此您不必自己指定。

如果装配中有任何带有签名的功能

“bool YourFunction(string something)”,它是一个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中的注释)以及非常通用的函数。看看我的generic, extensible string encoding function。这使用Func而不是Predicate委托,但目的是相同的。

答案 2 :(得分:3)

这样看:

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

将if evaluate子句中的位作为委托传递(托管等效于函数指针)。 RemoveAll方法展开列表并完成剩下的工作。

答案 3 :(得分:2)

它表示'删除b中包含的所有元素'。所以你只留下b中那个不存在于a中的那个。

答案 4 :(得分:1)

以下是代码的略微扩展版本,其中显示了正在发生的事情:

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

答案 5 :(得分:0)

对于b中的每个元素,正在为该元素评估a.Contains()。如果确实如此,则将其删除。

因此,您要删除b中包含的a中的每个元素。

函数“a.Contains”作为参数传递给RemoveAll

答案 6 :(得分:0)

RemoveAll的签名看起来像这样......

public int RemoveAll(Predicate<T> match);

Predicate<T>是一个代表,它接受Ts并返回bools ...

public delegate bool Predicate<T>(T obj)

因此,RemoveAll要求引用一个方法,在您的情况下,该方法将接受字符串并返回bool。 List<T>.Contains就是这样一种方法。您会注意到List<T>.Contains的签名与Predicate委托匹配(它需要Ts并返回bools)...

public bool Contains(T item);

RemoveAll会将谓词作为“匹配”传递给调用它的列表中的每个元素(在您的情况下为b)。因此,如果a.Contains("a")例如返回true,那么将从b列表中删除所有a。因此,在您的示例中,所有a,b和c都被删除。

使用像.NET Reflector这样的工具可以让你查看RemoveAll的代码,这可能有助于澄清幕后发生的事情。