.Net学习难点讨论系列10 - 匿名方法,Lambda表达式及其对局部变量的影响
2011-05-24 21:08
441 查看
匿名方法是C# 2.0中简化委托模型的一种语法糖。Lambda表达式是C# 3.0新增的语法特性,其在匿名方法的基础上更进一步,但其本质都是相同的,我们通过一段代码来分析对比这个语言特性。它们分别是在C#2.0与C# 3.0中的主要写法。
这篇文章主要介绍的一个问题是匿名方法中使用类成员或局部变量以及对匿名函数外部局部变量或参数的影响(对于lambda表达式同理,代码中有体现)。上述代码改造一下如下,我们通过这段代码来分析这个特性。
下列代码中需要注意的是匿名函数捕获变量的一个特殊情况(加粗代码),即局部变量的初始化问题,通过代码中两个对比应该可以很清楚的看出原因。
下面引用MSDN对匿名函数/Lambda表达式变量范围问题的总结:
delegate void DelWithoutParam (); delegate void DelWithParams (int intparam, string strparam); delegate string DelParamRetVal (int param); class Program { public DelWithoutParam delTestNoParam; public DelWithParams delTestWithParam; public DelParamRetVal delTestParamRetVal; public event DelWithoutParam evtTestNoParam; public event DelWithParams evtTestWithParam; static void Main(string [] args) { Program p = new Program (); // 订阅处理函数 p.SubEvent(); p.ShowVarible(); Console .Read(); // 依次触发委托与事件 p.delTestNoParam(); p.delTestWithParam(1, "ParamInDelobj" ); // 只会返回第二个调用函数的返回值 Console .WriteLine(p.delTestParamRetVal(10086)); p.evtTestNoParam(); p.evtTestWithParam(1, "ParamInEvt" ); //Console.ReadLine(); Thread .Sleep(100000); } public void SubEvent() { // 不具有参数的委托声明,在 delegate 后面加上 "()" delTestNoParam += delegate () { Console .WriteLine("del - 匹配不带参数的委托的方法被在匿名函数中调用 " ); }; evtTestNoParam += delegate () { Console .WriteLine("evt - 匹配不带参数的委托的方法被在匿名函数中调用 " ); }; // 匿名方法出现之前调用委托处理 evtTestNoParam += new DelWithoutParam (Program_evtTestNoParam); //lambda 表达式 evtTestNoParam += () => Console .WriteLine("evt - 匹配不带参数的委托的方法被在 lambda 表达式中调用 " ); // 有返回值的委托 delTestParamRetVal += delegate (int p) { return " 有返回值委托,在匿名函数中处理: " + p.ToString(); }; delTestParamRetVal += (q) => { return " 有返回值委托,在 lambda 表达式中处理: " + q.ToString(); }; } public void ShowVarible() { delTestWithParam += delegate (int int_p, string str_p) { Console .WriteLine("del - 匹配参数 int:{0},string:{1} 的委托的方法被在匿名函数中调用 " , int_p, str_p); }; evtTestWithParam += delegate (int int_p, string str_p) { Console .WriteLine("evt - 匹配参数 int:{0},string:{1} 的委托的方法被在匿名函数中调用 " , int_p, str_p); }; delTestWithParam += delegate { Console .WriteLine("del - 匹配任何参数的委托的方法被在匿名函数中调用 " ); }; //lambda 表达式 evtTestWithParam += (i, s) => Console .WriteLine("evt - 匹配参数 int:{0},string:{1} 的委托的方法被在 lambda 表达式中调用 " , i, s); } /* 辅助函数 */ void Program_evtTestNoParam() { Console .WriteLine("evt - 传统方式调用:匹配不带参数的委托的方法被在函数中调用 " ); } } |
这篇文章主要介绍的一个问题是匿名方法中使用类成员或局部变量以及对匿名函数外部局部变量或参数的影响(对于lambda表达式同理,代码中有体现)。上述代码改造一下如下,我们通过这段代码来分析这个特性。
下列代码中需要注意的是匿名函数捕获变量的一个特殊情况(加粗代码),即局部变量的初始化问题,通过代码中两个对比应该可以很清楚的看出原因。
delegate void DelWithoutParam (); delegate void DelWithParams (int intparam, string strparam); class Program { public DelWithoutParam delTestNoParam; public DelWithParams delTestWithParam; public string classmember = " 这是类成员 " ; static void Main(string [] args) { Program p = new Program (); // 订阅处理函数 p.SubEvent(); p.ShowVarible(); Console .Read(); // 依次触发委托与事件 p.delTestNoParam(); p.delTestWithParam(1, "ParamInDelobj" ); //Console.ReadLine(); Thread .Sleep(100000); } public void SubEvent() { // 匿名函数 - 调用成员变量示例 delTestNoParam += delegate () { Console .WriteLine(" 匿名函数中调用成员变量 :{0}" , classmember); }; //lambda 表达式 - 调用成员变量示例 delTestNoParam += () => Console .WriteLine("lambda 表达式中调用成员变量 :{0}" , classmember); int pd = 1; int pl = 2; // 匿名函数 - 调用局部变量 delTestNoParam += delegate () { // 由于匿名函数内部使用了局部变量,局部变量的生命周期增长,而不是立即被垃圾回收。 Console .WriteLine(" 匿名函数中调用局部变量 :{0}" , pd); }; //lambda 表达式 - 调用局部变量 delTestNoParam += () => Console .WriteLine("lambda 表达式中调用局部变量 :{0}" , pl); // 调用局部变量的特殊情况 - 匿名函数 for (int i = 0; i < 3; i++) { int j = i * 2 + 1; delTestNoParam += delegate () { Console .Write(" 匿名函数 - 变量定义于循环内,输出: " ); Console .Write(j + ", " ); Console .WriteLine(); }; } int k; for (int i = 0; i < 3; i++) { k = i * 2 + 1; delTestNoParam += delegate () { Console .Write(" 匿名函数 - 变量定义于循环外,输出: " ); Console .Write(k + ", " ); Console .WriteLine(); }; } // 调用局部变量的特殊情况 - lambda 表达式 for (int i = 0; i < 3; i++) { int j = i * 2 + 1; delTestNoParam += () => { Console .Write("lambda 表达式 - 变量定义于循环内,输出: " ); Console .Write(j + ", " ); Console .WriteLine(); }; } int m; for (int i = 0; i < 3; i++) { m = i * 2 + 1; delTestNoParam += () => { Console .Write("lambda 表达式 - 变量定义于循环外,输出: " ); Console .Write(m + ", " ); Console .WriteLine(); }; } } public void ShowVarible() { delTestWithParam += delegate (int int_p, string str_p) { Console .WriteLine("del - 匹配参数 int:{0},string:{1} 的委托的方法被在匿名函数中调用 " , int_p, str_p); }; //lambda 表达式 delTestWithParam += (i, s) => Console .WriteLine("evt - 匹配参数 int:{0},string:{1} 的委托的方法被在 lambda 表达式中调用 " , i, s); } } |
下面引用MSDN对匿名函数/Lambda表达式变量范围问题的总结:
下列规则适用于 Lambda 表达式中的变量范围: 捕获的变量将不会被作为垃圾回收,直至引用变量的委托超出范围为止。 在外部方法中看不到 Lambda 表达式内引入的变量。 Lambda 表达式无法从封闭方法中直接捕获 ref 或 out 参数。 Lambda 表达式中的返回语句不会导致封闭方法返回。 Lambda 表达式不能包含其目标位于所包含匿名函数主体外部或内部的 goto 语句、break 语句或 continue 语句。 |
相关文章推荐
- .Net学习难点讨论系列10 - 匿名方法,Lambda表达式及其对局部变量的影响
- .Net学习难点讨论系列6 - .Net中对字符串处理的方法
- .Net学习难点讨论系列9 -泛型类型参数的约束 泛型方法
- .Net学习难点讨论系列6 - .Net中对字符串处理的方法
- .Net学习难点讨论系列6 - .Net中对字符串处理的方法
- .Net学习难点讨论系列17
- .Net学习难点讨论系列2 – 细说C#中new关键字与多态
- .Net学习难点讨论系列1 – 委托与事件之事件
- .Net学习难点讨论系列2 – 细说C#中new关键字与多态
- .net学习之新语法学习(匿名类和匿名方法,扩展方法,系统内置委托,Lambda表达式和linq等)
- .Net学习难点讨论系列3 – .线程同步问题之一
- .Net学习难点讨论系列4 – .Net委托类型
- .Net学习难点讨论系列7 - .NET委托补充
- 学习系列之匿名方法与lambda表达式
- .Net学习难点讨论系列8 - 泛型字典类比较
- .Net学习难点讨论系列5 – 线程同步问题之二
- .Net学习难点讨论系列12 - 垃圾回收
- .Net学习难点讨论系列7 - .NET委托补充
- .Net学习难点讨论系列5 – 线程同步问题之二
- .Net学习难点讨论系列15 - 小技巧总结