我有多个布尔语句正在评估。它们按从上到下的顺序进行评估。一些布尔语句的评估成本很高,所以我对它们使用惰性。这是示例代码:
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;
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,最昂贵的语句是在惰性对象中。我可以用 Func 做同样的事情。使用其中一种的优缺点是什么?
无论是Lazy也不Func是魔术。它们提供的唯一功能是您推迟相关代码的执行。它不会使它更快,它不会使它并行,它只是稍后。
使用延迟执行的主要好处是在某些情况下可以完全避免执行。例如,如果你有一段这样的代码:
a() && b() && c()
Run Code Online (Sandbox Code Playgroud)
您知道只有评估b()和c()何时a()返回 true才有意义。的延迟执行b()和c()从未发生过。当您将中间结果存储在 locals 中时,您将失去此属性(例如,为了提高可读性):
var resultOfA = a();
var resultOfB = b();
var resultOfC = c();
...
return resultOfA && resultOfB && resultOfC;
Run Code Online (Sandbox Code Playgroud)
不再有任何机会推迟 的执行b(),除非您使用像var resultOfB = resultOfA && b();. 但是,您也可以内联要评估的函数,而不是要评估的函数的结果。
在 C# 中执行此操作的最简单方法是使用函数委托:
Func<bool> fa = () => a();
Func<bool> fb = () => b();
Func<bool> fc = () => c();
...
return fa() && fb() && fc();
Run Code Online (Sandbox Code Playgroud)
但是,这意味着,fa例如,无论何时执行,都会a()一次又一次地进行评估。这可能是需要的,也可能不是。如果它不想要,你就来到了Lazy.
Lazy 允许您推迟执行,但缓存执行结果,以便对惰性对象的任何后续访问都将仅使用原始延迟执行的结果,而不是再次评估“构造函数”函数。
所以,总结一下:
Func无论上下文如何,您都可以使用延迟执行(并可能避免执行),因此您可以轻松地组合复杂的短路模式。Lazy延迟执行(并可能避免它),同时还缓存任何后续对惰性值的评估的结果。这对于具有非平凡范围的长期对象特别有用。在您的示例代码中,最佳选择可能是 1) - 当您可以使用短路时,获得函数委托的开销毫无意义。此处延迟执行的主要好处是允许您命名表达式的各个部分 - 这样您就可以得到 eghasPermission() && isInRole() && isSunny()而不是难以阅读的表达式。如果您最多只使用表达式的每个部分一次,那么使用是没有意义的Lazy——它只是意味着在底层Func.
如果您仅使用布尔条件一次 - 在您的情况下,Lazy 和 Func 之间似乎没有太大区别。但是,如果您确实多次使用它们,那么当然 Lazy 是更好的选择 - 它实际上只会计算一次值,并在下次调用时返回缓存的值。
请注意,因为布尔条件已经被延迟计算,所以您可以这样做:
bool permit = contactHasPermission || IsContactInRole(contactId, roleType) || ContactHasXPoints(contactId, 100)
Run Code Online (Sandbox Code Playgroud)
并且 if 具有相同的效果 - 除非必要,否则不会评估昂贵的调用。但是,如果您想这样做以提高可读性,并且不重复使用布尔条件 - 请使用 Func。
| 归档时间: |
|
| 查看次数: |
1758 次 |
| 最近记录: |