我正在评估多个 bool 语句。它们按从上到下的顺序进行评估。一些 bool 语句的评估成本很高,所以我对它们使用了惰性。这是示例代码:
bool contactHasPermission = ContactHasPermission(id);
var contactIsInRole = new Lazy<bool>(() => IsContactInRole(contactId, roleType)); //Database call
var contactHasAtLeastXPoints = new Lazy<bool>(() => ContactHasXPoints(contactId, 100)); //database call
bool permit = contactHasPermission || contactIsInRole.Value || contactHasAtLeastXPoints.Value;
如您所见,最昂贵的语句位于惰性对象中。我可以用 Func 做同样的事情。使用其中一种的优缺点是什么?
最佳答案
Lazy
和 Func
都不是魔法。它们唯一提供的是您可以推迟相关代码的执行。它不会使其更快,也不会使其并行,它只是稍后。
使用延迟执行的主要好处是在某些情况下您可以完全避免执行。例如,如果您有这样一段代码:
a() && b() && c()
您知道只有当 a()
返回 true 时才对 b()
和 c()
求值才有意义。 b()
和 c()
的延迟执行永远不会发生。当您将中间结果存储在本地时(例如,为了提高可读性),您将失去此属性:
var resultOfA = a();
var resultOfB = b();
var resultOfC = c();
...
return resultOfA && resultOfB && resultOfC;
不再有任何机会延迟 b()
的执行,除非您使用像 var resultOfB = resultOfA && b();
这样可怕的代码。但是,您也可以内联要计算的函数,而不是要计算的函数的结果。
在 C# 中执行此操作的最简单方法是使用函数委托(delegate):
Func<bool> fa = () => a();
Func<bool> fb = () => b();
Func<bool> fc = () => c();
...
return fa() && fb() && fc();
但是,这意味着无论何时执行 fa
,例如,您都会一次又一次地评估 a()
。这可能是需要的,也可能不是。如果不需要,您将来到 Lazy
的世界。
Lazy
允许您延迟执行,但缓存执行结果,以便任何后续访问延迟对象将只使用原始延迟执行的结果,而不是评估“构造函数” "再次运行。
所以,总结一下:
- 您可以使用短路来避免执行 bool 表达式中的表达式。
- 无论上下文如何,您都可以使用
Func
来延迟执行(并可能避免执行),这样您就可以轻松编写复杂的短路模式。 - 您可以使用
Lazy
来延迟执行(并可能避免它),同时还可以缓存结果以用于对 lazy 值的任何后续评估。这对于具有非平凡范围的长期对象特别有用。
在您的示例代码中,最好的选择可能是 1) - 当您可以使用短路时,没有必要获得函数委托(delegate)的开销。此处延迟执行的主要好处是允许您命名表达式的部分 - 这样您就可以得到例如hasPermission() && isInRole() && isSunny()
而不是难以阅读的表达式。如果您最多只使用表达式的每个部分一次,那么使用 Lazy
就没有意义了 - 它只是意味着围绕底层 Func
的额外样板。
关于c# - 何时使用 Lazy 与 Func,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37812542/