短路逻辑和延迟加载
一、短路逻辑
短路逻辑(short-circuit logic)也叫懒惰求值(lazy evaluation),在我们熟知的布尔运算中有一个非常有趣的特性:只有在需要求值时才进行求值。举例来说,ConditionA() and ConditionB()需要两个条件都为真时才是真,所以如果ConditionA() 为假,表达式立刻返回false,而不会去计算ConditionB()造成不必要的运算浪费。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static void Main( string [] args) { if (ConditionA() && ConditionB()) { System.Console.WriteLine( "ConditionA() && ConditionB() is true" ); } System.Console.WriteLine( "short-circuit logic test end" ); System.Console.Read(); } static bool ConditionA() { System.Console.WriteLine( "ConditionA:false" ); return false ; } static bool ConditionB() { System.Console.WriteLine( "ConditionB:true" ); return true ; } |
输出:
ConditionA:false
short-circuit logic test end
如你看到的那样,ConditionA()为假,那么ConditionB()就被“短路”了,根本不需要去执行。这种“只有在需要求值时才进行求值”的特点体现了“按需”的思想,可以看做是一种朴素的延迟加载。
二、延迟加载(执行)
在实际开发中延迟加载的思想应用非常普遍,比如图片的按需加载、某些ORM实现的查询对关联数据的加载等等。那么什么是延迟加载呢?
百度百科解释的超级烂,但是我们都可以理解,延迟加载体现了“按需创建要使用的资源”这一核心思想。在.NET中有不少按需分配(Load On Demand)延迟加载的实现机制。下面结合实际开发试举两个常见的延迟加载的例子。
1、yield和Linq
yield关键字的使用记得以前总结过,可参考这一篇。
yield语句只能出现在iterator(迭代器)块中,我们可以把一些耗时的或者需要延迟的操作放在MoveNext()里面。正是yield才使Linq的延迟执行成为可能。
在Linq中,所有实现了IEnumerable<T>接口的类都被称作sequence(序列),Linq最基本的数据单元是sequences和elements。一个sequence是实现了IEnumerable<T>的对象,而一个element是sequence中的每一个元素。如下就是一个简单的Linq查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class UserInfo { public string Name { get ; set ; } } class Program { static void Main( string [] args) { var lang = "python" ; var list = new List<UserInfo> { new UserInfo{Name= "jeffwong" }, new UserInfo{Name= "anytao" }, new UserInfo{Name= "dudu" } }; var query = list.Where(m => m.Name.StartsWith( "j" )); //linq查询 if (lang == "c#" ) { foreach ( var item in query) { Console.WriteLine(item.Name); } } System.Console.Read(); } } |
上面示例代码中有个if条件,就是当语言(lang)等于C#的时候输出序列中的用户(元素)的名称。但是其实这个条件为假,所以对于linq查询结果query根本没有什么作用,如果这是一个耗时的查询操作,偏偏查询结果又用不到,那不是白做了吗?好在在Linq查询实现中,query变量只是一个可迭代列表(IEnumerable<T>)对象占位符,它根本没有进行计算,也没有加载数据(这里是用户信息)进入内存,只有在foreach遍历取值的时候才会进行运算取值,显然可以节省开销。
当然,上例中我们构造的列表数据量很小,而且本身就在内存中,就算执行了查询,好像也看不出性能上有多少提升。但是实际上在我们熟知的Linq to Sql中,IQueryable<T>就是继承实现了IEnumerable<T>接口,使用Linq To Sql访问数据库,对于网络传输和IO的影响就不得不考虑,否则很容易造成查询缓慢用户体验恶劣。好在我们可以利用Linq的延迟执行机制写出良好的查询,而且微软的Linq To Sql和ADO.NET Entity Framework框架都支持对关联数据的延迟加载机制。
2、Lazy<T>
.NET中典型的延迟加载按需创建对象的类就是Lazy<T>,对于那种“懒对象”的创建非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class UserInfo { public string Name { get ; set ; } } class Program { static void Main( string [] args) { Lazy<UserInfo> lazyUser = new Lazy<UserInfo>(() => new UserInfo { Name = "jeffwong" }); if (!lazyUser.IsValueCreated) //用户还没有创建 { Console.WriteLine( "User is not created yet." ); } Console.WriteLine(lazyUser.Value.Name); //此时真正创建用户 System.Console.Read(); } } |
实际上Lazy<T>可以给开发者提供更多的控制选项(比如支持委托)和线程安全。
参考:
http://msdn.microsoft.com/en-us/library/dd642331.aspx
http://msdn.microsoft.com/en-us/library/9k7k7cf0(v=vs.100).aspx
作者:Jeff Wong
出处:http://jeffwongishandsome.cnblogs.com/
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2009-08-05 sql的临时表使用小结