将 Predicate<T> 转换为 Func<T, bool>

29

我有一个类,其中有一个成员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<T> 等价于 Func<T, bool>"时(参见这里),我期望这段代码能够正常工作,因为All的参数是:Func<Instrument, bool> predicate
然而,我却收到了以下错误提示:
Argument 2: cannot convert from 'System.Predicate<MyNamespace.Instrument>' to 'System.Type'

有没有一种方法可以将谓词转换为该函数可以接受的参数?
6个回答

24
两种类型表示相同的逻辑签名,但这并不意味着它们可以互换。例如,直接赋值是行不通的,但是你可以从 Predicate 创建一个新的 Func。示例代码:
Predicate<string> pred = x => x.Length > 10;
// Func<string, bool> func = pred; // Error
Func<string, bool> func = new Func<string, bool>(pred); // Okay

这有点像有两个具有相同值的枚举类型 - 你可以在它们之间进行转换,但必须明确地这样做。它们仍然是不同的类型。
在你的情况下,这意味着你可以这样写:
public bool DoAllHaveSomeProperty()
{
    return m_instrumentList.All(new Func<T, bool>(m_filterExpression));
}

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

这个 new Func<T, bool>(predicate)x => predicate(x) 好多了!谢谢! - florien

16
public bool DoAllHaveSomeProperty()
{
    return m_instrumentList.All(i => m_filterExpression(i));
}

啊哈,可以了,谢谢!所以 Predicate<T> 和 Func<T,bool> 显然在语义上并不完全相同! - Yellow
哎呀,那就是我想说的。 :-) - Yellow
很不幸,这会带来一些开销,只是提醒一下。我已经测量过了,它不能被编译或即时编译器优化掉。 - jackmott

13
你可以通过调用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;
}

7
return m_instrumentList.All(i => m_filterExpression(i));

1

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

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

1
正如Brian所说,您可以通过Invoke将谓词转换为方法:
public bool DoAllHaveSomeProperty()
{
    return m_instrumentList.All(m_filterExpression.Invoke);
}

1
那么为什么要重复呢? - Gert Arnold
@GertArnold,他们的答案提供了很多细节(我很尊重),解决问题的实际方案看起来有些受阻。我决定提供一个更简洁的答案版本,这样更容易阅读。也许我应该建议修改现有的答案。 - Arthur Khazbs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接