Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegate Combination Misbehavior

Tags:

c#

delegates

Give the following piece of code:

List<string> aux = new List<string>();

aux.Add("a");
aux.Add("ab");
aux.Add("ac");
aux.Add("abc");
aux.Add("b");
aux.Add("bc");
aux.Add("c");
aux.Add("e");
aux.Add("f");

Func<string, bool> searchForA = (f => f.Contains("a"));
Func<string, bool> searchForC = (f => f.Contains("c"));
Func<string, bool> searchForF = (f => f.Contains("f"));

Can someone please explain to me why this call

aux.Where(searchForA + searchForF + searchForC).ToList();

returns "ac", "abc", "bc" and "c" which is the same result as

aux.Where(searchForC).ToList();

I mean, wheres "a" and "ab" and "F" on the first query?

EDIT: I used the delegate combination because I want to define dynamically the search pattern!

EDIT2: Major edit check for new piece of example code, this the problem im tryng to solve

string[] searchFor = "a c f".Split(' ');

Func<string, bool>[] delegates = new Func<string, bool>[searchFor.Length];
for (int i = 0; i < searchFor.Length; i++)
{
    string search = searchFor[i]; // Make sure the lambda does not capture a loop variable!
    delegates[i] = new Func<string, bool>(f => f.Contains(search));
}

List<string> aux = new List<string>(); 
aux.Add("a");
aux.Add("ab");
aux.Add("ac");
aux.Add("abc");
aux.Add("b");
aux.Add("bc");
aux.Add("c");
aux.Add("e");
aux.Add("f");

List<string> result = aux.Where((Func<string, bool>)Delegate.Combine(delegates)).ToList();
like image 277
Leonardo Avatar asked Jul 23 '26 01:07

Leonardo


1 Answers

Invoking the combination of two non-void returning delegates has the semantics "call the first, discard the result, call the second, return the result". It doesn't have the semantics "call them both and or the results if they happen to return bool". Remember, delegate combination has to work for any delegate type; why should the runtime choose or semantics for bool-returning delegates instead of and semantics? What should it do for delegates that return string, concatenate the strings? If you combine int returning delegates should it add the numbers?

The problem of combining results is not solvable in a consistent way, so the choice is to ignore all but the last.

If you want to or two predicates together then you can easily do so yourself. Here's a handy extension method:

static Func<T, bool> Or<T>(this Func<T, bool> f1, Func<T, bool> f2)
{ return t => f1(t) || f2(t); }

and now you can say

...Where(searchForA.Or(searchForC).Or(searchForF))...

You could also extend that to multiple delegates:

static Func<T, bool> OrMany<T>(params Func<T, bool>[] fs)
{ 
    Func<T, bool> result = t => false;
    foreach(Func<T, bool> f in fs)
        result = result.Or(f);
    return result;
}

And now you can use "expanded form" with a list of delegates:

...Where(OrMany(searchForA, searchForC, searchForf))...

or "unexpanded form" with an array of delegates:

Func<string, bool>[] delegates = whatever;
...Where(OrMany(delegates))...
like image 135
Eric Lippert Avatar answered Jul 25 '26 15:07

Eric Lippert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!