我正在评估多个布尔语句。它们按从上到下的顺序进行评估。一些布尔语句的评估代价很高,所以我对它们使用了懒惰。以下是示例代码:
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做同样的事情。使用一个优于另一个的利弊是什么?
答案 0 :(得分:3)
在您的特定情况下,Func
和Lazy
都不合理。 C#短路评估,所以
bool permit = contactHasPermission ||
IsContactInRole(contactId, roleType) ||
ContactHasXPoints(contactId, 100);
会很好:如果contactHasPermission
是true
,则不会进行数据库调用。
答案 1 :(得分:3)
如果你只使用你的布尔条件一次 - 在你的情况下,Lazy和Func之间似乎没有太大的区别。但是,如果你多次使用它们,那么当然Lazy是可取的 - 它实际上只计算一次值,并在下次调用时返回缓存值。
请注意,由于布尔条件已经延迟评估,您可以这样做:
bool permit = contactHasPermission || IsContactInRole(contactId, roleType) || ContactHasXPoints(contactId, 100)
如果具有相同的效果 - 除非必要,否则不会评估昂贵的电话。但是,如果您想为可读性执行此操作,并且不重用布尔条件,请使用Func。
答案 2 :(得分:3)
Lazy
和Func
都不是魔法。他们唯一提供的是你推迟执行有问题的代码。它不会使它更快,它不会使它平行,它只是以后。
使用延迟执行的主要好处在于,在某些情况下,可以完全避免执行。例如,如果您有一段这样的代码:
a() && b() && c()
您知道在b()
返回true时评估c()
和a()
才有意义。 b()
和c()
的延迟执行从未发生过。将中间结果存储在本地时,会丢失此属性(例如,为了提高可读性):
var resultOfA = a();
var resultOfB = b();
var resultOfC = c();
...
return resultOfA && resultOfB && resultOfC;
除非您使用像b()
这样的可怕代码,否则不再有机会推迟var resultOfB = resultOfA && b();
的执行。但是,您也可以内联要评估的函数,而不是要评估的函数的结果。
在C#中执行此操作的最简单方法是使用函数委托:
Func<bool> fa = () => a();
Func<bool> fb = () => b();
Func<bool> fc = () => c();
...
return fa() && fb() && fc();
但是,这意味着每当您执行fa
时,您都会反复评估a()
。这可能是需要与否。如果不需要,你来到Lazy
的世界。
Lazy
允许你推迟执行,但是缓存执行的结果,以便对延迟对象的任何后续访问只使用原始延迟执行的结果,而不是再次评估“构造函数”函数
所以,总结一下:
Func
来推迟执行(并可能避免执行),这样您就可以轻松编写复杂的短路模式。Lazy
来推迟执行(并可能避免执行),同时还可以缓存结果,以便对延迟值进行任何后续评估。这对于具有非平凡范围的长寿命对象特别有用。在您的示例代码中,最佳选择可能是1) - 当您可以使用短路时,没有必要获得函数代理的开销。这里延迟执行的主要好处是允许你命名表达式的一部分 - 这样你就可以得到hasPermission() && isInRole() && isSunny()
而不是难以阅读的表达。如果你只使用表达式的每个部分一次,那么使用Lazy
是没有意义的 - 它只是意味着在底层Func
周围有额外的样板。
答案 3 :(得分:0)
如果您将Lazy
实例缓存为成员变量,那么可以延迟评估,从而提高性能。但是,如果你不是那么,就没有什么可以获得的了。你不妨说:
bool permit = ContactHasPermission(id) ||
IsContactInRole(contactId, roleType) ||
ContactHasXPoints(contactId, 100);
||
的短路评估将确保您只评估所需的最低费用,从而避免随后的昂贵电话。
答案 4 :(得分:0)
我知道这个问题很老,但还没有正确回答。所以这里。
检查此fiddle上的示例。
使用Lazy在需要时稍后创建对象的实例,但是一旦被调用,它就会在Value属性中保存该对象的实例。无法更改此实例。
类似地,Func延迟创建对象的实例,但是每次调用Func时,您都将获得该对象的新实例。
因此,在只需要一个对象的延迟实例的地方使用Lazy,并使用Func在每个调用中创建对象的新实例。
在您的情况下,如果您每次都必须重新评估条件,则应使用Func,但是如果您只想评估条件一次并多次使用结果,则可以使用Lazy。
希望这有帮助。