您的位置:首页 > 编程语言 > C#

.Net学习难点讨论系列10 - 匿名方法,Lambda表达式及其对局部变量的影响

2011-05-24 21:08 441 查看
匿名方法是C# 2.0中简化委托模型的一种语法糖。Lambda表达式是C# 3.0新增的语法特性,其在匿名方法的基础上更进一步,但其本质都是相同的,我们通过一段代码来分析对比这个语言特性。它们分别是在C#2.0与C# 3.0中的主要写法。

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 语句。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息