Tom Bennett
Tom Bennett

Reputation: 137

Compile Error 'cannot convert' on Declared Delegate

I'm having issues when passing a delegate as a Where parameter in LINQ.

I'm declaring a delegate in the namespace:

public delegate bool FilterInt<in T>(T x);

I've made a method to assign to the delegate and then assign it:

    public static bool FilterData(int val)
    {
        if (val < 10)
            return true;
        return false;

    }

When I try to filter a List using the delegate I get a compile time error 'Cannot conver from FilterInt to Func<Int, bool>:

listInt.Where(_filterer);

However, I can use the below (an implied delegate?) without any issues:

listInt.Where(FilterData);

I have the same issue with _comparer when I follow the MSDN doc on delegates here if I define my own delegate in the namespace as per the below:

public delegate int Comparison<in T>(T x, T y);

public static int CompareLength(string left, string right) =>
     right.Length.CompareTo(left.Length);

readonly Comparison<string> _comparer = CompareLength;

list.Sort(_comparer);

However, if I omit the initial declaration of Comparison, it works fine (note - Comparison exists in the System namespace).

I assume it's an issue with my initial delegate declaration.

Upvotes: 0

Views: 355

Answers (3)

The error occurs because of the where clause expects a Func<TSource, TResult> with a Boolean as TResult and not just a Func<int> as its representative.

IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

The closest way to use it as you wish will be something like this, but I don't believe it as a good implementation.

using System;
using System.Collections.Generic;
using System.Linq;

public delegate Func<T1, T2> FilterInt<in T1, out T2>(T1 value);

namespace DelegateTest
{
    class Program
    {
        private static readonly List<int> List = new List<int> {0, 1, 2, 10, 11, 12};

        static void Main(string[] args)
        {
            FilterInt<int, bool> filterInt = FilterData;
            var result = List.Where(x => filterInt.Invoke(x).Invoke(x));
            foreach (var item in result) Console.WriteLine(item.ToString());
        }

        private static Func<int, bool> FilterData(int value) => _ => value < 10;
    }
}

Upvotes: 1

Guru Stron
Guru Stron

Reputation: 142233

Enumerable.Where accepts Func<TSource, bool> delegate. If you look for documentation on Func<T,TResult> you will find out that it is declared as:

public delegate TResult Func<in T,out TResult>(T arg);

So you will need to "convert" your delegate into instance of Func<TSource, bool> which is possible either with direct creation an instance of the delegate:

new[] { 1 }.Where(new Func<int, bool>(_filterer));

Or with anonymous (lambda) function:

new[] { 1 }.Where(i => _filterer(i));

Which is actually a syntactic sugar transformed by compiler into something like this:

 // generated class to hold your lambda:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public FilterInt<int> _filterer;
    // your lambda: 
    internal bool <M>b__0(int i)
    {
        return _filterer(i);
    }
}

   
// instantiation of new Func<int, bool>
Enumerable.Where(array, new Func<int, bool>(<>c__DisplayClass0_.<M>b__0));

Method group call (listInt.Where(FilterData);) also is syntactic sugar expanded by compiler into creation of new delegate:

Enumerable.Where(array, new Func<int, bool>(FilterData));

Upvotes: 1

Vivek Nuna
Vivek Nuna

Reputation: 1

You can call like this.

Where(new Func<int, bool>(FilterInt))

Or

.Where(x => FilterInt(x))

Upvotes: 1

Related Questions