28

I have a class with a member Predicate which I would like to use in a Linq expression:

using System.Linq;

class MyClass
{

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

    private IEnumerable<Instrument> m_instrumentList;

    private Predicate<Instrument> m_filterExpression;
}

As I read that "Predicate<T> is [...] completely equivalent to Func<T, bool>" (see here), I would expect this to work, since All takes in as argument: Func<Instrument, bool> predicate.

However, I get the error:

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

Is there a way to convert the predicate to an argument that this function will swallow?

Community
  • 1
  • 1
Yellow
  • 3,955
  • 6
  • 45
  • 74

6 Answers6

23

The two types represent the same logical signature, but that doesn't mean they're just interchangable. A straight assignment won't work, for example - but you can create a new Func<T, bool> from the Predicate<T, bool>. Sample code:

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

This is a bit like having two enum types with the same values - you can convert between them, but you have to do so explicitly. They're still separate types.

In your case, this means you could write:

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

The lambda expression approach suggested by other answers will work too, of course.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
16
public bool DoAllHaveSomeProperty()
{
    return m_instrumentList.All(i => m_filterExpression(i));
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
George Vovos
  • 7,563
  • 2
  • 22
  • 45
13

You can convert a predicate to a method by calling Invoke. All delegates have this member. Delegates don't have structural identity, but methods can be converted to matching delegates. This fix has a minor performance cost, as it adds an extra layer of indirection. However, most solutions to this problem have that problem. Eric Lippert discusses this in more detail at https://web.archive.org/web/20140625132124/http://blog.coverity.com/2014/06/18/delegates-structural-identity/ .

In your specific case, replace return m_instrumentList.All(m_filterExpression); with return m_instrumentList.All(m_filterExpression.Invoke);

Sample code demonstrating the actual problem.

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;
}
Brian
  • 25,523
  • 18
  • 82
  • 173
7
return m_instrumentList.All(i => m_filterExpression(i));
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
1

Since there are a lot of answers i will add one more just for fun. If you want your code to compile you can use extention methods

//Original Code
class MyClass4
{
    public bool DoAllHaveSomeProperty()
    {
        return m_instrumentList.All(m_filterExpression);
    }

    private IEnumerable<Instrument> m_instrumentList;

    private Predicate<Instrument> m_filterExpression;
}

Add this class in the same namespace

public static class MyExtentions
{
    public static bool All(this IEnumerable<Instrument> enumer, Predicate<Instrument> pred)
    {
        return enumer.All(e => pred(e));
    }
}
George Vovos
  • 7,563
  • 2
  • 22
  • 45
1

As Brian said, you can convert a predicate to a method via Invoke:

public bool DoAllHaveSomeProperty()
{
    return m_instrumentList.All(m_filterExpression.Invoke);
}
Arthur Khazbs
  • 705
  • 8
  • 19
  • 1
    So why repeat it? – Gert Arnold Oct 02 '20 at 07:30
  • @GertArnold Their answer provides a lot of details (which I respect) and the actual solution to the problem looks obstructed. I decided to put a more concise version of the answer so it would be easier to read. Maybe I should have suggested an edit to the existing answer instead. – Arthur Khazbs Oct 05 '20 at 22:57