何时使用Lazy vs Func

时间:2016-06-14 12:49:41

标签: c#

我正在评估多个布尔语句。它们按从上到下的顺序进行评估。一些布尔语句的评估代价很高,所以我对它们使用了懒惰。以下是示例代码:

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做同样的事情。使用一个优于另一个的利弊是什么?

5 个答案:

答案 0 :(得分:3)

在您的特定情况下,FuncLazy都不合理。 C#短路评估,所以

bool permit = contactHasPermission || 
  IsContactInRole(contactId, roleType) || 
  ContactHasXPoints(contactId, 100);

会很好:如果contactHasPermissiontrue,则不会进行数据库调用。

答案 1 :(得分:3)

如果你只使用你的布尔条件一次 - 在你的情况下,Lazy和Func之间似乎没有太大的区别。但是,如果你多次使用它们,那么当然Lazy是可取的 - 它实际上只计算一次值,并在下次调用时返回缓存值。

请注意,由于布尔条件已经延迟评估,您可以这样做:

bool permit = contactHasPermission || IsContactInRole(contactId, roleType) || ContactHasXPoints(contactId, 100)

如果具有相同的效果 - 除非必要,否则不会评估昂贵的电话。但是,如果您想为可读性执行此操作,并且不重用布尔条件,请使用Func。

答案 2 :(得分:3)

LazyFunc都不是魔法。他们唯一提供的是你推迟执行有问题的代码。它不会使它更快,它不会使它平行,它只是以后

使用延迟执行的主要好处在于,在某些情况下,可以完全避免执行。例如,如果您有一段这样的代码:

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允许你推迟执行,但是缓存执行的结果,以便对延迟对象的任何后续访问只使用原始延迟执行的结果,而不是再次评估“构造函数”函数

所以,总结一下:

  1. 您可以使用短路来避免在布尔表达式中执行表达式。
  2. 无论上下文如何,您都可以使用Func来推迟执行(并可能避免执行),这样您就可以轻松编写复杂的短路模式。
  3. 您可以使用Lazy来推迟执行(并可能避免执行),同时还可以缓存结果,以便对延迟值进行任何后续评估。这对于具有非平凡范围的长寿命对象特别有用。
  4. 在您的示例代码中,最佳选择可能是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。

希望这有帮助。