Expression<Func<T,bool>>与Func<T,bool>的方法重载差异及FakeItEasy使用疑问
Great question! Let's break down the key differences between Expression<Func<T, bool>> and Func<T, bool> in these FakeItEasy Matches overloads, plus when to reach for each one.
Core Differences
First, let's clarify what each delegate type does, specifically in the context of FakeItEasy argument constraints:
1. Func<T, bool>: The "Just Run It" Delegate
- This is a compiled, executable piece of code. When FakeItEasy checks if an argument matches your constraint, it runs the lambda/function directly to get a true/false result.
- FakeItEasy can't "look inside" this delegate to understand what you're checking—it only knows whether the argument passed the test. That's why these overloads require you to provide a custom description string: so you can document what the constraint is checking for.
- Example usage:
// Using a lambda with a custom description A.CallTo(() => myService.ProcessOrder(A<Order>.That.Matches(o => o.Total > 100, "order total exceeds $100"))); // Using a named function bool IsHighValueOrder(Order o) => o.Total > 100 && o.IsPriority; A.CallTo(() => myService.ProcessOrder(A<Order>.That.Matches(IsHighValueOrder, "high-value priority order")));
2. Expression<Func<T, bool>>: The "Parseable" Expression Tree
- This isn't executable code right away—it's a data structure that describes the logic of your lambda (like an abstract syntax tree). FakeItEasy can parse this tree to understand exactly what condition you're checking.
- Because it can parse the expression, FakeItEasy automatically generates a human-readable description of your constraint. No need to write a custom string! It also uses this parsed info to create more detailed error messages when a call doesn't match your expectation.
- Example usage:
If this call fails to match, FakeItEasy will output an error like:A.CallTo(() => myService.ProcessOrder(A<Order>.That.Matches(o => o.Total > 100 && o.IsPriority)));Expected call with argument matching
o => (o.Total > 100) AndAlso o.IsPriority, but received Order { Total = 80, IsPriority = True }.
When to Use Each Overload
Pick Func<T, bool> when:
- Your constraint logic is complex (e.g., involves loops, calls to non-trivial custom methods, or conditional logic that's hard to represent as a simple expression tree).
- You want full control over the description of the constraint (maybe you need more context than the auto-generated string can provide).
Pick Expression<Func<T, bool>> when:
- Your constraint is a simple, declarative condition (property comparisons, basic method calls that FakeItEasy can parse).
- You want cleaner, less verbose code (no need to write a custom description).
- You need detailed, self-documenting error messages to debug test failures faster.
Quick Note on Performance
Expression trees do require a small amount of parsing overhead when the test runs, compared to directly executing a Func<T, bool>. But in almost all testing scenarios, this difference is negligible—don't let it drive your choice unless you're running an extremely high-volume test suite.
内容的提问来源于stack exchange,提问作者mbudnik

