【问题标题】:Converting a Predicate<T> to a Func<T, bool>将 Predicate<T> 转换为 Func<T, bool>
【发布时间】:2014-08-27 15:58:45
【问题描述】:

我有一个成员 Predicate 的班级,我想在 Linq 表达式中使用它:

using System.Linq;

class MyClass
{

    public bool DoAllHaveSomeProperty()
    {
        return m_instrumentList.All(m_filterExpression);
    }

    private IEnumerable<Instrument> m_instrumentList;

    private Predicate<Instrument> m_filterExpression;
}

当我读到“Predicate&lt;T&gt; [...] 完全等同于 Func&lt;T, bool&gt;”(see here)时,我希望这会起作用,因为 All 接受为参数:Func&lt;Instrument, bool&gt; predicate

但是,我得到了错误:

Argument 2: cannot convert from 'System.Predicate<MyNamespace.Instrument>' to 'System.Type'

有没有办法将谓词转换为该函数将吞下的参数?

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    这两种类型代表相同的逻辑签名,但这并不意味着它们可以互换。例如,直接分配是行不通的——但您可以从Predicate&lt;T, bool&gt; 创建一个新的Func&lt;T, bool&gt;。示例代码:

    Predicate<string> pred = x => x.Length > 10;
    // Func<string, bool> func = pred; // Error
    Func<string, bool> func = new Func<string, bool>(pred); // Okay
    

    这有点像拥有两个具有相同值的enum 类型——您可以在它们之间进行转换,但您必须明确地这样做。它们仍然是不同的类型。

    在你的情况下,这意味着你可以写:

    public bool DoAllHaveSomeProperty()
    {
        return m_instrumentList.All(new Func<T, bool>(m_filterExpression));
    }
    

    当然,其他答案建议的 lambda 表达式方法也可以。

    【讨论】:

    • 这个new Func&lt;T, bool&gt;(predicate) 比说x =&gt; predicate(x) 好多了!谢谢!
    【解决方案2】:
    public bool DoAllHaveSomeProperty()
    {
        return m_instrumentList.All(i => m_filterExpression(i));
    }
    

    【讨论】:

    • 啊哈,这行得通,谢谢!所以 Predicate 和 Func 显然不是完全相同,至少在语义上不一样!
    • 糟糕,我就是这个意思。 :-)
    • 不幸的是,这有一些开销,仅供参考。我测量了它,它不会编译或 JIT 消失。
    【解决方案3】:

    您可以通过调用 Invoke 将谓词转换为方法。所有代表都有这个成员。委托没有结构标识,但可以将方法转换为匹配的委托。此修复程序的性能成本很小,因为它增加了一个额外的间接层。然而,这个问题的大多数解决方案都有这个问题。 Eric Lippert 在https://web.archive.org/web/20140625132124/http://blog.coverity.com/2014/06/18/delegates-structural-identity/ 对此进行了更详细的讨论。

    在您的具体情况下,将return m_instrumentList.All(m_filterExpression); 替换为return m_instrumentList.All(m_filterExpression.Invoke);

    演示实际问题的示例代码。

    void Main()
    {
        Predicate<int> t1 = Foo;
        Func<int,bool> t2 = Foo;
        Predicate<int> t3 = t2.Invoke; //Legal
        Func<int,bool> t4 = t1.Invoke; //Legal
        Predicate<int> t5 = t2; //Illegal
        Func<int,bool> t6 = t1; //Illegal
    }
    
    bool Foo(int x)
    {
        return x > 20;
    }
    

    【讨论】:

      【解决方案4】:
      return m_instrumentList.All(i => m_filterExpression(i));
      

      【讨论】:

        【解决方案5】:

        由于有很多答案,我会再添加一个,只是为了好玩。 如果你想让你的代码编译你可以使用扩展方法

        //Original Code
        class MyClass4
        {
            public bool DoAllHaveSomeProperty()
            {
                return m_instrumentList.All(m_filterExpression);
            }
        
            private IEnumerable<Instrument> m_instrumentList;
        
            private Predicate<Instrument> m_filterExpression;
        }
        

        在同一个命名空间中添加这个类

        public static class MyExtentions
        {
            public static bool All(this IEnumerable<Instrument> enumer, Predicate<Instrument> pred)
            {
                return enumer.All(e => pred(e));
            }
        }
        

        【讨论】:

          【解决方案6】:

          正如 Brian 所说,您可以通过 Invoke 将谓词转换为方法:

          public bool DoAllHaveSomeProperty()
          {
              return m_instrumentList.All(m_filterExpression.Invoke);
          }
          

          【讨论】:

          • 那为什么要重复呢?
          • @GertArnold 他们的回答提供了很多细节(我尊重),并且问题的实际解决方案看起来受阻。我决定放一个更简洁的答案版本,这样更容易阅读。也许我应该建议对现有答案进行编辑。
          猜你喜欢
          • 2012-04-05
          • 2010-10-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-31
          相关资源
          最近更新 更多