C#之lambda表达式
从C#3.0开始,可以使用lambda表达式把实现代码赋予委托。lambda表达式与委托(http://www.cnblogs.com/afei-24/p/6762442.html)直接相关。当参数是委托类型时,就可以使用lambda表达式实现委托引用。
static void Main() { string mid = ", middle part,"; Func<string, string> anonDel = param => { param += mid; param += " and this was added to the string."; return param; }; Console.WriteLine(anonDel("Start of string")); }
lambda运算符“=>” 的左边是参数列表,右边是lambda变量的方法的实现代码。
1.参数
如果lambda表达式只有一个参数,只写出参数名就可以,像上面的代码。
如果委托使用多个参数,就需要把参数名放到括号中:
Func<double, double, double> twoParams = (x, y) => x * y;
Console.WriteLine(twoParams(3, 2));
可以在括号中给变量名添加参数类型:
Func<double, double, double> twoParamsWithTypes = (double x, double y) => x * y;
Console.WriteLine(twoParamsWithTypes(4, 2));
2.多行代码
如果lambda表达式只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐形的return语句。
Func<double, double, double> twoParams = (x, y) => x * y;
Func<double, double, double> twoParams = (x, y) =>
{
retrun x * y;
}
如果在lambda表达式的实现代码中有多条语句,就必须添加花括号和return语句:
Func<string, string> anonDel = param =>
{
param += mid;
param += " and this was added to the string.";
return param;
};
3.闭包
通过lambda表达式可以访问lambda表达式块外部的变量,这称为闭包。闭包是一个很好的功能,但如果使用不当,会很危险。例如:
int someVal = 5;
Func<int,int> f = x => x+someVal;
假定以后修改了变量someVal,于是调用委托f时,会使用someVa的新值:
someVal = 7;
f(3);//结果为10而不是8.
特别是,通过另一个线程调用lambda表达式时,我们可能不知道进行了这个调用,也不知道外部变量的当前值是什么。
所以在使用闭包时,一定要谨慎!!!
在lambda表达式访问lambda表达式块外部的变量时,编译器在定义lambda表达式时,编译器会创建一个匿名类,它用一个构造函数来传递外部变量。该构造函数取决于从外部传递进来的变量个数和类型。
对于lambda表达式Func<int,int> f = x => x+someVal;
public class AnonymousClass { private int someVal; public AnonymousClass(int someVal) { this.someVal = someVal; } public int AnonymousMethod(int x) { retrun x+someVal; } }
使用lambda表达式并调用该方法的时,会创建匿名类的一个实例,并传递调用该方法时变量的值。
4.使用foreach语句的闭包
先看下面这个例子:
var values = new List<int>() {10,20,30};
var funcs = new List<Func<int>>();
foreach(var val in values)
{
funcs.Add(() => val);
}
foreach(var f in funcs)
{
Console.WriteLine((f()));
}
第一条foreach语句添加了funcs列表中每个元素。添加到列表中的函数使用lambda表达式。该lambda表达式使用了一个变量val,该变量在lambda表达式的外部定义为foreach语句的循环变量。第二条foreach语句迭代funcs列表,以调用列表中引用的每个函数。
在C#5.0之前版本编译这段代码时,会在控制台输出30三次。这是因为,在第一个foreach循环中使用闭包,所创建的函数是在调用时,而不是在迭代时获得val变量的值。在http://www.cnblogs.com/afei-24/p/6738155.html中介绍foreach时讲到编译器会从foreach语句中创建一个while循环。在C#5.0之前版本中,编译器在while循环外部定义循环变量,在每次迭代中重用这个变量。因此,在循环结束时,该变量的值就是最后一次迭代时的值。要想在使用C#5.0之前版本时,输出10,20,30,需要将代码改为使用一个局部变量:
var values = new List<int>() {10,20,30};
var funcs = new List<Func<int>>();
foreach(var val in values)
{
var v = val;
funcs.Add(() => v);
}
foreach(var f in funcs)
{
Console.WriteLine((f()));
}
在C#5.0中,不再需要做这种代码修改。C#5.0会在while循环的代码中创建一个不同的局部循环变量。
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的