1

Following 3 methods are part of a library I use, FakeItEasy:

public static T Matches<T>(this IArgumentConstraintManager<T> scope, Func<T, bool> predicate, string description);

public static T Matches<T>(this IArgumentConstraintManager<T> manager, Func<T, bool> predicate, string descriptionFormat, params object[] args);

public static T Matches<T>(this IArgumentConstraintManager<T> scope, Expression<Func<T, bool>> predicate);

I have following problem when using Visual Studio 2017. I want to provide a predicate as a Func<T,bool> not Expression<Func<T, bool>>. The reason for that is my function compares a dynamic property of a class and looks like this:

 A.CallTo(() => A<Class>.That.Matches(obj => obj.DynamicProperty == "text")).MustHaveHappened();

DynamicProperty is marked with a dynamic keyword and I'm getting compile time error saying

An expression tree may not contain dynamic operation

https://i.stack.imgur.com/FoQdO.png Ok, I get it. It's not supported in expressions. I would like to choose an overload that uses Func<T, bool>.

If there were two methods that would just differ in the type of predicate (expression and func) how do I choose one over another?

As you can see in above methods definitions the Func<T,bool> overload has an additional string description parameter. I would expect that this would be enough for compiler to choose a proper overload.

So I've changed my call to be like this:

A.CallTo(() => A<Class>.That.Matches(obj => obj.DynamicProperty == "text", "testing")).MustHaveHappened();

Still getting the same compiler time error but when I hover over the method in Visual Studio now the tooltip says the method chosen is the Func<T,bool> one. So why I still cannot compile it with an error saying something about expression trees? I don't want to use any expression trees!

So I've managed to get it to compile only if I put the predicate in a separate method

public bool Test(Class obj){
    return obj.DynamicProperty == "text";
}

And change the my check to be

A.CallTo(() => A<Class>.That.Matches(Test, "testing")).MustHaveHappened();

This seems to do the trick.

But I would still expect the compiler to treat the function I wrote using lamda syntax to be treated as a Func<T,bool> not Expression<Func<T,bool>>.

Is this a bug in VS or the compiler or am I missing something?

EDIT - steps/setup to reproduce it

Create a blank console project replace packages.config and Program.cs:

https://gist.github.com/michaelbudnik/33f3dd39df038ba1d02f01dc9659002b

mbudnik
  • 2,087
  • 15
  • 34
  • by defining the method the type is already known to be `Func` it should also work if you define the function in a variable before the call like so `var test = new func((class)=> {class.DynamicProperty == "text";});` then call the match passing the function – Franck Apr 06 '18 at 13:03
  • But without doing that the lambda could be both `Func` and `Expression>`. So why the compiler doesn't warn you about this ambiguity and chooses method with an expression parameter? Even after adding an additional parameter which should be a discriminator it still complains about not supported dynamic properties in expressions – mbudnik Apr 06 '18 at 13:09
  • If there are two methods with one parameter one having `Expression` and the other `Func` the compiler returns ambiguity error. – arekzyla Apr 06 '18 at 13:12
  • What does this have to do with Fluent Assertions. This post talks about FakeItEasy. – Dennis Doomen Apr 06 '18 at 13:13
  • 1
    @DennisDoomen That's right, sorry. Edited tags – mbudnik Apr 06 '18 at 13:15
  • I cannot reproduce this. Behaves as expected for me (such as adding "testing" makes it resolve to Func overload). – Evk Apr 06 '18 at 13:21
  • @Evk I've just tried it on a blank project and minimal setup and it suprisingly seems fine. This is what I get in my original solution: https://i.imgur.com/ujetxPh.png – mbudnik Apr 06 '18 at 13:33
  • Well you need to somehow come up with reproducable example then, hard to suggest someting useful with given info. Try to "navigate to definition" (F12) on that Matches call to see what exactly is being resolved. – Evk Apr 06 '18 at 13:39
  • @Evk Edited and added easy steps to reproduce. – mbudnik Apr 06 '18 at 13:56

1 Answers1

3

The expression that the compiler is complaining about is the argument to "CallTo" method:

A.CallTo(() => mandrill.SendMessageTemplate(A<SendMessageTemplateRequest>.That.Matches(...)));

So it doesn't matter which overload of Matches is chosen.

GC.
  • 1,214
  • 1
  • 10
  • 26