17

I try to compile the following code in C#:

public static T FirstEffective(IEnumerable<T> list) 
{
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault(list, pred);
}

The compiler (Mono/.NET 4.0) gives the following error:

File.cs(139,47) The best overloaded method match for `System.Linq.Enumerable.FirstOrDefault<T>(this System.Collections.Generic.IEnumerable<T>,System.Func<T,bool>)' has some invalid arguments
/usr/lib/mono/4.0/System.Core.dll (Location of the symbol related to previous error)
File.cs(139,47): error CS1503: Argument `#2' cannot convert `System.Predicate<T>' expression to type `System.Func<T,bool>'

This is rather strange since a Predicate<T> is in fact a function that takes as input a parameter T and returns a bool (T is even "covariant" thus a specialization of T is allowed). Do delegates do not take the "Liskov Substitution principle" into account to derive that Predicate<T> is equivalent to Func<T,bool>? As far as I know this equivalence problem should be decidable.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Take a look at http://stackoverflow.com/questions/665494/why-funct-bool-instead-of-predicatet – racraman Aug 25 '14 at 03:49
  • This is related, but does not explain why equivalence checks are not performed. As argued, the equivalence of delegate types is decidable... – Willem Van Onsem Aug 25 '14 at 03:50
  • 1
    As an aside to your question: Is it in fact known that the C# type system is not Turing complete? Is it a requirement that a type system be Turing-complete for it to be undeciable? The general problem of subtyping is undecidable; this paper might interest you: http://research.microsoft.com/pubs/64041/fool2007.pdf. When it was written C# did not yet have contravariance but it does now, so the bits about the IL language apply to C# 4.0. – Eric Lippert Aug 25 '14 at 14:19
  • @EricLippert: Sorry, the words were indeed chosen poorly. Many thanks. – Willem Van Onsem Aug 25 '14 at 17:30

4 Answers4

20

C# specification is clear about that:

15.1 Delegate declarations

Delegate types in C# are name equivalent, not structurally equivalent. Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types.

That's why your code doesn't compile.

You can make it work by calling the delegate, instead of passing it:

public static T FirstEffective (IEnumerable<T> list) {
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault (list, x => pred(x));
}

Update

There is a great blog post by Eric Lippert: former member of C# Team as Microsoft, which answers your question in much details: Delegates and structural identity.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Still wondering why that is. Since the conversion between delegates is quite trivial: there is no real transformation. The same "method pointer" would be sufficient. – Willem Van Onsem Aug 25 '14 at 03:54
  • 4
    That's exactly the difference between method pointers and C# delegates. It's the same with `class`es: two classes with the same set of members are not considered equivalent. That's how type system in .NET works. – MarcinJuraszek Aug 25 '14 at 03:55
  • 2
    Eric's blog link is dead. Best I could find was using archive.org https://web.archive.org/web/20160331233359/http://blog.coverity.com/2014/06/18/delegates-structural-identity/ – pinkfloydx33 Jul 27 '17 at 12:36
6

Delegate types are not implicitly convertible, even when they have all the same parameter and return information. There is an easy workaround for your case though. You can use the .Invoke method on your delegate instance.

public static T FirstEffective<T>(IEnumerable<T> list)
{
    Predicate<T> pred = x => x != null;
    return Enumerable.FirstOrDefault(list, pred.Invoke);
}

As to the question of why delegates work this way, the answer is that it was a design decision. Classes that have identical public interfaces aren't implicitly convertible either, so it's not really inconsistent.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
recursive
  • 83,943
  • 34
  • 151
  • 241
  • That's a nice solution. But it's rather strange that delegates are not considered to be equivalent... – Willem Van Onsem Aug 25 '14 at 03:52
  • yes but that's because they have different fields,... and it is not trivial how to convert them. A `List` that uses an array is completely different from a list that uses a `LinkedList` approach. – Willem Van Onsem Aug 25 '14 at 03:53
  • 1
    If I make two classes that each only have a `public string Foo { get; set; }`, I won't be able to use them interchangeably even their definitions are identical. I see this as being a similar case. – recursive Aug 25 '14 at 03:54
  • 1
    yes @recursive is right - this is a common behaviour in OOP languagues - the name of a class/interface/etc. matters, not the signature. – Random Dev Aug 25 '14 at 04:12
  • @recursive: there is a difference: the programming code in C# is Turing complete. It is thus undecidable to check if two classes are identical (of course this trivial example can be checked). The type system however is not Turing complete, thus one can check if two signatures are identical... – Willem Van Onsem Aug 25 '14 at 04:15
1

Quite belated but coincidentally I stumbled up on the same question and the accurate answer can be found here: important comment

Basically it means that it's an inconsistency based on an unfortunate decision to implement it this way. Whilst predicate<T> == func<T, bool>, they are different types despite the same signature. I suppose for reasons of backward compatibility one can convert an expression and/or lambda and then return a predicate through new predicate<T>(func<T, bool>).

Community
  • 1
  • 1
Valmont
  • 328
  • 1
  • 2
  • 11
0

Using the predicate worked when applied to a list using enumerable

  List<Product> products = new List<Product>
            {
                new Product {ProductID=1,Name="Kayak",Category="Watersports",Price=275m},
                new Product {ProductID=2,Name="Lifejacket", Category="Watersports",Price=48.95m},
                new Product {ProductID=3,Name="Soccer Ball", Category="Soccer",Price=19.50m},
                new Product {ProductID=4,Name="Corner Flag", Category="Soccer",Price=34.95m}
            };

Predicate<Product> predicate = x => x.ProductID==4;

            var query=Enumerable.FirstOrDefault(products,x=>predicate(x));

            if (query != null) { 
                output.WriteLine(query.Name);
                Assert.True(true);
            }
Golden Lion
  • 3,840
  • 2
  • 26
  • 35