The question Can I define a method to accept EITHER a Func OR an Expression<Func>? asks basically the same thing, but it was asked and answered in 2010 and C# has changed quite a bit since then.
I have a filter method, SelectItems
, that accepts a Func<Thing, bool>
, where Thing
is an individual item in a collection and the bool
return value is whether or not the item passes the filter.
SelectItems
is used in multiple places in the codebase. Some places pass expression lambdas and others pass statement lambdas.
If no items in the collection pass the filter, it throws an exception with an exception message that describes the collection. Because this code is referenced in many places, I would like to add a description of the filter itself to the exception message - something like Item matching filter "(item) => item % 2 == 0" was not found. Items found: 1, 3, 5, 7
.
If the filter function accepts an Expression<Func<Thing, bool>>
, I can print the function itself with a PrettyPrintCSharpCode
method. There are probably other ways to pull information from an Expression
, but pretty printing the code would suffice for now. If a Func
is passed, though, the message will not be able to include the code. This is fine, and probably the biggest difference between the question I linked above and mine.
If all calls to this function only passed expression lambdas, I could simply change the function argument to an Expression
and it would work implicitly. The problem I've run into is that many of the calls pass statement lambdas, so those calls break.
What I would like is to be able to pass either an expression lambda or a statement lambda.
I tried overloading the function (with one overload accepting Func and the other an Expression), but the compiler is unable to properly pass the expression lambdas to the function that accepts an Expression
and the statement lambdas to the function that excepts a Func
.
I also tried optional arguments (something like SelectItems(Func<Thing, bool> func = null, Expression<Func<Thing, bool>> expr = null)
) but it had the same issue.
If I overloaded a function to accept an int
or a double
, the compiler would figure out the best fit. I'm confused that it doesn't do that for this.
Specifically, the code
class ClassName
void SelectItems(Expression<Func<Thing, bool>> filterExpression) {}
void SelectItems(Func<Thing, bool> filterFunc) {}
generates this compiler error
void ClassName.SelectItems(Func<Thing, bool> filterFunc) (+ 1 overload)
CS0121: The call is ambiguous between the following methods or properties:
'ClassName.SelectItems(Func<Thing, bool>)' and
'ClassName.SelectItems(Expression<Func<Thing, bool>>)'
Ambiguous invocation:
void SelectItems(System.Func<Thing, bool>) (in class ClassName)
void SelectItems(System.Linq.Expression<System.Func<Thing, bool>>) (in class ClassName)
match
Is there a way to do this?