【问题标题】:Odd Lambda Expression Question奇怪的 Lambda 表达式问题
【发布时间】:2011-01-02 07:43:54
【问题描述】:
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。任何人都可以扩展实际发生的事情,因为目前它根本没有任何意义。

编辑:我更多的是谈论谓词的使用。如何知道如何将什么传递到哪里?

【问题讨论】:

  • 伙计,我很难再像新手一样思考了。这对我来说很有意义。
  • 值得一提的是,在 .NET 2.0(和“C# 2.0”)之前,这个 sn-p 不起作用...编译器正在从 a.Contains 方法创建一个匹配的委托。在 2.0 之前的模式下,您必须自己创建委托 (new Predicate(a.Contains))
  • 嗯,List 类在 .NET 2.0 之前也不存在,但这不是我的意思。

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


【解决方案1】:
b.RemoveAll(<function that takes a string and returns true if we want to remove it>)

不需要 lambda 表达式。

也许您希望阅读该行

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

然而,函数 x=&gt; a.Contains(x) 只是一个简单的函数,它接受一个字符串并返回一个布尔值,指示a 是否包含x。 a.Contains 已经是一个可以做到这一点的函数。

【讨论】:

    【解决方案2】:

    { } 语法是一个集合初始化器。代码相当于

    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 是一个接受字符串并返回布尔值的函数。所以代码可以改成:

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

    请注意,在这个 Lambda 语法中,您传递的是“a.Contains”函数——而不是函数的结果!它就像一个函数指针。 RemoveAll 期望采用“bool FunctionName(string input)”形式的函数。

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

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

    • 谓词:接受 T 并返回布尔值的委托。
    • Action:接受 1 到 4 个参数并返回 void 的委托
    • 功能:一个接受0到4个参数并返回一个T的委托

    (无耻抄袭 Jon Skeet 的Answer here)

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

    如果你的程序集中有任何带有签名的函数

    “bool YourFunction(string something)”,它是一个Predicate&lt;string&gt;,可以传递给任何其他接受一个的函数:

    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 委托,但目的是一样的。

    【讨论】:

      【解决方案3】:

      这样看:

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

      您将 if 评估子句中的位作为委托(函数指针的托管等效项)传递。 RemoveAll 方法展开列表并完成其余的工作。

      【讨论】:

      • Lambda 是那些带有 lambda 运算符的东西,也就是奇怪的箭头:=&gt;。 a.Contains 是一个方法组,编译器会自动为其创建一个委托,该委托属于 RemoveAll (Predicate&lt;string&gt;) 所期望的类型。
      【解决方案4】:

      它说'删除 b 中包含在 a 中的所有元素'。所以你只剩下 b 中没有出现在 a 中的一个。

      【讨论】:

        【解决方案5】:

        这是您的代码的略微扩展版本,它显示了正在发生的事情:

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            【解决方案7】:

            RemoveAll 的签名如下所示...

            public int RemoveAll(Predicate<T> match);
            

            Predicate&lt;T&gt; 是一个接受 Ts 并返回布尔值的委托 ...

            public delegate bool Predicate<T>(T obj)
            

            RemoveAll 因此要求引用一个方法,在您的情况下,该方法将获取字符串并返回布尔值。 List&lt;T&gt;.Contains就是这样一种方法。您会注意到 List&lt;T&gt;.Contains 的签名与 Predicate 委托匹配(它需要 Ts 并返回 bools)...

            public bool Contains(T item);
            

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-10-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-05-09
              • 1970-01-01
              相关资源
              最近更新 更多