条件表达式的短路求值与函数的延迟求值
延迟求值是 .NET的一个很重要的特性,在LISP语言,这个特性是依靠宏来完成的,在C,C++,可以通过函数指针来完成,而在.NET,它是靠委托来完成的。如果不明白什么是延迟求值的同学,我们先看看下面的一段代码:
static void TestDelayFunction()
{
TestDelayFunton1(true,trueFun3);
}
static void TestDelayFunton1(bool flag , Func<bool> fun )
{
if(flag)
fun();
}
在方法 TestDelayFunton1 中,函数型参数 fun 是否求值,取决于第一个参数 flag,如果它的值为false,那么函数 fun 是永远都不会被求值的,所以,这里函数 fun的求值被推迟到了方法TestDelayFunton1 的内部,而不是在参数计算的时候。
延迟求值很有用,它可以避免我们无谓的计算,比如上面的例子,这样可以节省计算成本,假如 fun的求值很耗时的话。
我们注意这一段代码:
if(flag)
fun();
其实它等价于一个逻辑表达式:
bool result= flag && fun();
在这个表达式中,fun() 函数是否求值,取决于变量 flag,这个功能叫做“短路”判断,“条件短路”功能正好实现了我们的“延迟求值”的功能,因此,我们可以得到如下推论:
任何时候一个函数fun如果需要延迟求值,那么都可以表示成 一个条件表达式:
(Test() && fun())
所以,前面的2个函数,本质上可以改写成下面的一个函数:
static void TestDelayFunton2(bool flag)
{
bool result = flag && trueFun3();
}
它将 TestDelayFunton1(true,trueFun3); 的形式调用,转换成了上面的一个函数调用。
当然,要让这种调用变得可用,我们还需要解决一个问题,就是函数 fun()的类型并不是 bool类型,这个问题处理很简单,将函数再包装下即可:
bool WarpFunction()
{
fun();
return true;
}
之后的调用将是这个样子的:
(Test() && WarpFunction())
对于本例,它其实等价于:
(flag && trueFun3())
如果是“聪明”的编译器,它是可以完成上面的转换的,下面给出一个完整的代码图片,这样你能够看得更清楚:
上面被标记的部分的2个函数,等价于下面这一个函数,也就是说,TestDelayFunton1 的调用变换成了 TestDelayFunton2的调用。
如果你对上面的这个过程还是不太明白,那么我们看看下面这个例子:
static bool trueFun1()
{
Console.WriteLine("call fun 1");
return true;
}
static bool falseFun2()
{
Console.WriteLine("call fun 2");
return false;
}
static bool trueFun3()
{
Console.WriteLine("call fun 3");
return true;
}
执行下面的代码,trueFun3都会被执行么?
if (trueFun1() && falseFun2() && (trueFun3()))
{
}
Console.WriteLine();
if (trueFun1() || falseFun2() || trueFun3())
{
}
假如你非常理解C#的“条件短路”特性,相信答案很快就出来了。
阅读完本文,你可能会问如此奇淫巧技,有何作用?
如果你深入研究.NET的委托,就会明白委托调用其实是将一个函数用对象进行包装,.NET自动为你生成了很多代码,性能上必然有所损耗,假如你在某些地方需要性能极致的代码,那么本文这个技巧一定可以帮助你,假如你还能够写出一个这种转换的编译器来,恭喜你,未来的大神就是你了!
- delete相关的pl/sql调优(r4笔记第87天)
- Java文件上传与下载【面试+工作】
- QBC查询
- 一条delete语句的调优(r4笔记第86天)
- Java支付宝接口开发【面试+工作】
- 03.SVN检出/解决冲突/提交
- Spring思维导图,让Spring不再难懂(mvc篇)
- SQL优化一(SQL使用技巧)
- Spring思维导图,让Spring不再难懂(aop篇)
- MongoDB初探第二篇 (r4笔记第82天)
- Spring思维导图,让Spring不再难懂(cache篇)
- 曲折的10g,11g中EM的安装配置过程(r4笔记第98天)
- Linux 学习记录 一(安装、基本文件操作).
- 实用的位运算应用(r4笔记第97天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- CentOS桌面环境中网卡启动失败的解决方法
- 浅谈Linux vfork与fork简单对比分析
- Linux定时任务Crontab的使用方法
- linux虚拟网络设备之vlan配置详解
- Centos7安装完后无法联网的解决方法
- Linux静态链接库与模板类的处理方式
- 深入研究RocketMQ消费者是如何获取消息的
- CentOS7下 Apache的安装配置方法
- expect命令在linux下实现批量ssh免密
- 学习Vim合并行的方法和技巧
- 九种查找算法
- 代码防御性编程的十条技巧
- Ubuntu终端多窗口分屏Terminator
- linux 编译安装python3.6的教程详解
- Unix/Linux系统下的nobody用户与nologin详细介绍