当前位置: 首页 > 编程日记 > 正文

C#2.0匿名函数

C# 2.0中提供了通过delegate实现匿名函数功能,能有效地减少用户记代码工作,例如


以下为引用:

...
button1.Click += new EventHandler(button1_Click);
...
void button1_Click(Object sender, EventArgs e) {
// Do something, the button was clicked...
}
...




可以被简化为直接使用匿名函数构造,如


以下为引用:

...
button1.Click += delegate(Object sender, EventArgs e) {
// Do something, the button was clicked...
}
...




关于匿名函数的使用方法可以参考Jeffrey Richter的Working with Delegates Made Easier with C# 2.0一文。简要说来就是C#编译器自动将匿名函数代码转移到一个自动命名函数中,将原来需要用户手工完成的工作自动完成。例如构造一个私有静态函数,如


以下为引用:

class AClass {
static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(delegate(Object obj) { Console.WriteLine(obj); }, 5);
}
}




被编译器自动转换为


以下为引用:

class AClass {
static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(new WaitCallback(__AnonymousMethod$00000002), 5);
}

private static void __AnonymousMethod$00000002(Object obj) {
Console.WriteLine(obj);
}
}




而这里自动生成的函数是否为static,编译器根据使用此函数的地方是否static决定。这也是为什么C# 2.0规范里面禁止使用goto, break和continue语句从一个匿名方法里跳出,或从外面跳入其中的原因,因为他们代码虽然写在一个作用域里面,但实际上实现上并不在一起。
更方便的是编译器可以根据匿名函数使用的情况,自动判断函数参数,无需用户在定义时指定,如


以下为引用:

button1.Click += delegate(Object sender, EventArgs e) { MessageBox.Show("The Button was clicked!"); };




在不使用参数时,完全等价于


以下为引用:

button1.Click += delegate { MessageBox.Show("The Button was clicked!"); };





相对于匿名函数的实现来说,比较复杂的是匿名函数对于其父作用域中变量的使用及其实现。MS的Grant Ri在其blog上有一系列的讨论文章。
Anonymous Methods, Part 1 of ?
Anonymous Methods, Part 2 of ?
Anonymous Method Part 2 answers

需要解决的问题有两个:一是不在一个变量作用域中的匿名函数如何访问父函数和类的变量;二是匿名函数使用到的变量的生命周期必须与其绑定,而不能与父函数的调用生命周期绑定。这两个问题使得C#编译器选择较为复杂的独立类封装方式实现匿名函数和相关变量生命周期的管理。

首先,匿名函数使用到的父函数中局部变量,无聊是引用类型还是值类型,都必须从栈变量转换为堆变量,以便在其作用域外的匿名函数实现代码可以访问并控制生命周期。因为栈变量的生命周期与其所有者函数是一致的,所有者函数退出后,其堆栈自动恢复到调用函数前,也就无法完成变量生命周期与函数调用生命周期的解耦。
例如下面这个简单的匿名函数中,使用了父函数的局部变量,虽然此匿名函数只在父函数里面使用,但C#编译器还是使用独立类对其使用到的变量进行了包装。


以下为引用:

delegate void Delegate1();

public void Method1()
{
int i=0;

Delegate1 d1 = delegate() { i++; };

d1();
}




自动生成的包装代码类似如下


以下为引用:

delegate void Delegate1();

private sealed class __LocalsDisplayClass$00000002
{
public int i;

public void __AnonymousMethod$00000001()
{
this.i++;
}
};

public void Method1()
{
__LocalsDisplayClass$00000002 local1 = new __LocalsDisplayClass$00000002();
local1.i = 0;

Delegate1 d1 = new Delegate1(local1.__AnonymousMethod$00000001);

d1();
}




但对于有多个局部变量作用域的情况就比较复杂了,例如Grant Ri在其例子中给出的代码


以下为引用:

delegate void NoArgs();

void SomeMethod()
{
NoArgs [] methods = new NoArgs[10];
int outer = 0;
for (int i = 0; i < 10; i++)
{
int inner = i;
methods[i] = delegate {
Console.WriteLine("outer = {0}", outer++);
Console.WriteLine("i = {0}", i);
Console.WriteLine("inner = {0}", ++inner);
};
methods[i]();
}
for (int j = 0; j < methods.Length; j++)
methods[j]();
}




就需要一个类封装变量outer;一个类封装变量i;另外一个类封装inner和匿名函数,并引用前面两个封装类的实例。因为变量outer、i和inner有着不同的作用域,呵呵。伪代码如下:


以下为引用:

private sealed class __LocalsDisplayClass$00000008
{
public int outer;

};
private sealed class __LocalsDisplayClass$0000000a
{
public int i;

};
private sealed class __LocalsDisplayClass$0000000c
{
public int inner;

public __LocalsDisplayClass$00000008 $locals$00000009;
public __LocalsDisplayClass$0000000a $locals$0000000b;

public void __AnonymousMethod$00000007()
{
Console.WriteLine("outer = {0}", this.$locals$00000009.outer++);
Console.WriteLine("i = {0}", this.$locals$0000000b.i);
Console.WriteLine("inner = {0}", ++this.inner);
}
};

public void SomeMethod()
{
NoArgs [] methods = new NoArgs[10];

__LocalsDisplayClass$00000008 local1 = new __LocalsDisplayClass$00000008();
local1.outer = 0;

__LocalsDisplayClass$0000000a local2 = new __LocalsDisplayClass$0000000a();
local2.i = 0;

while(local2.i < 10)
{
__LocalsDisplayClass$0000000c local3 = new __LocalsDisplayClass$0000000c();
local3.$locals$00000009 = local1;
local3.$locals$0000000b = local2;
local3.inner = local1.i;

methods[local2.i] = new NoArgs(local3.__AnonymousMethod$00000007);
methods[local2.i]();
}

for (int j = 0; j < methods.Length; j++)
methods[j]();
}





总结其规律就是每个不同的局部变量作用域会有一个单独的类进行封装,子作用域中如果使用到父作用域的局部变量,则子作用域的封装类引用父作用域的封装类。相同作用域的变量和匿名方法由封装类绑定到一起,维护其一致的生命周期。

相对于MS较为复杂的实现,Delphi.NET对嵌套函数则使用较为简单的参数传递方式,因为嵌套函数没有那么复杂的变量生命期管理要求,如


以下为引用:

procedure SayHello;
var
Name: string;

procedure Say;
begin
WriteLn(Name);
end;
begin
Name := 'Flier Lu';

Say;
end;




系统生成函数Say代码时,将使用到的上级变量如Name放入到一个自动生成的类型($Unnamed1)中,然后作为函数参数传递给Say函数,伪代码类似


以下为引用:

type
$Unnamed1 = record
Name: string;
end;

procedure @1$SayHello$Say(var UnnamedParam: $Unnamed1);
begin
WriteLn(UnnamedParam.Name);
end;

procedure SayHello;
var
Name: string;
Unnamed1: $Unnamed1;
begin
Name := 'Flier Lu';

Unnamed1.Name := Name;

Say(Unnamed1);
end;

相关文章:

第三届北京智源大会开幕,全球最大智能模型“悟道2.0”重磅发布

6月1日&#xff0c;由北京智源人工智能研究院&#xff08;以下简称智源研究院&#xff09;主办的2021北京智源大会在北京中关村国家自主创新示范区会议中心成功开幕。 北京智源大会是智源研究院主办的年度国际性人工智能高端学术交流活动&#xff0c;定位于“AI内行顶级盛会”…

Cloneable接口和循环冗余校验算法

1 Cloneable 接口 实现该接口的类可以调用clone()方法合法地对该类实例进行按字段复制。按照惯例&#xff0c;实现此接口的类应该使用公共方法重写 Object.clone&#xff08;它是受保护的&#xff09; 2 循环冗余校验算法 循环冗余校验&#xff08;英语&#xff1a;Cyclic redu…

IOS自定义表格UITableViewCell

在UITableView中&#xff0c;自定义表格&#xff0c;最原始是继承UITableViewCell&#xff0c;然后通过写代码方式去搞&#xff0c;但是这个费事了。 1.在storyboard中 给一个ViewController的tabieview增加自定义的UITableViewCell&#xff0c;可以直接从 object Library里面选…

postfix邮件服务器搭建

项目环境&#xff1a;一台server ip:192.168.1.100 一台mail ip:192.168.1.200 一台win7 ip:192.168.1.222项目需求&#xff1a;1&#xff1a;在DNS Master上搭建DNS&#xff0c;能够解析mail.sw.com2&#xff1a;在Mail Server上部署邮件服务器&#xff0c;和webmail软件3…

从DataView中生成Excel报表的方案(C#)

正文&#xff1a; 一、首先要引用一个Excel的组件&#xff0c;我一开始是在Office XP下尝试的&#xff0c;不 成功&#xff0c;后来把XP给干掉&#xff0c;装2k&#xff0c;就成功了&#xff0c;所以这里分享的是Office 2k下 引用相关组件来实现功能的&#xff0c;在工程中引…

被Python「苦虐」的日子太惨了!

Python因为其优越的特性广泛应用于数据分析、人工智能、Web开发、后端开发、自动化测试/运维、爬虫等领域&#xff0c;也得到了很多企业的青睐。甚至连BATZJ的技术大牛&#xff0c;都无可否认Python现在对于一个程序员发展的重要性&#xff01;最近一两年&#xff0c;我身边也有…

BeanUtils威力和代价

2019独角兽企业重金招聘Python工程师标准>>> BeanUtils: 威力和代价&#xff08;转载综合&#xff09; Apache Jakarta Commons项目非常有用。我曾在许多不同的项目上或直接或间接地使用各种流行的commons组件。其中的一个强大的组件就是BeanUtils。我将说明如何使…

C# 线程无法开启窗口的原因

在 C# 里面, 主窗口拥有主线程, 主线程产生子线程监控 Socket 埠, 子线程一收到数据流就会给主线程发送一个事件, 创建一个窗口. 现在的情况是子线程能够收到数据流, 主窗口能够收到子线程发送过来的事件, 能够创建一个窗口. 这个窗口有问题: 窗口状态像死掉程序的窗口一样, 反…

给力!斩获 GitHub 14000 Star,两周创办开源公司获数百万美元融资

作者 | 伍杏玲出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09;上世纪 90 年代初&#xff0c;21 岁大学生 Linus Torvalds 开源 Linux 操作系统&#xff0c;自此掀起全球开源浪潮。随后“中国 Linux 第一人”宫敏博士用手提肩背的方式将 20 盒磁带背回中国&#xff0…

root密码忘记怎么办

开机按e进入系统的紧急求援模式依次输入一下命令&#xff1a;mount -o remount&#xff0c;rw /sysrootchroot /sysrootecho "输入新密码" | passwd --stdin rootexitreboot转载于:https://blog.51cto.com/11552940/1971850

C#内容分页简单实现代码及祥解

//定义变量 int i,start,stop,t,stat,statt,pp,pagecount,pagesize; //变量初始值 stat0; statt0; start0;//开始查询的字符串位置&#xff0c;初始为0 stop0; pagesize2000;//定义每页至少显示字符串数 pagecount0; //获得当前的页数 paRequest.Params["page&qu…

灵活越障、花样空翻,腾讯轮腿式机器人亮相机器人顶会 ICRA

6月3日&#xff0c;腾讯轮腿式机器人Ollie&#xff08;奥利&#xff09;正式亮相。它像一个灵活的“轮滑小子”&#xff0c;能完成跳跃、360度空翻等高难度动作。 轮腿式机器人&#xff08;wheel-legged robot&#xff09;是近年来机器人研究的前沿领域。Ollie兼具轮式结构和腿…

android学习笔记之十服务(Service)

2019独角兽企业重金招聘Python工程师标准>>> Service拥有一个单独进程的模块. 1,继承自Service类,须实现public IBinder onBind(Intent intent) 2,通过startServie触发运行,stopService终止运行 生命周期: onCreate(如果是第一次运行) -> onStart -> onDest…

宝塔Linux常用命令

https://www.bt.cn/bbs/thread-1186-1-1.html 2017年3月8日发布全新架构的宝塔Linux 面板3.1Beta版&#xff0c;到现在的5.2.0正式版&#xff0c;历经100多天打磨&#xff0c;宝塔研发工程师做了大量优化适配&#xff0c;从最开始的只支持CentOS到现在支持CentOS、Ubuntu、Debi…

EMAIL发送系统(C#+基于SMTP认证) 2.0

这个是对于 EMAIL发送系统(C#基于SMTP认证) 1.0 的改版这个第一版是2002年11月写的&#xff0c;过了一年半了&#xff0c;才有人提意见&#xff0c;就修正了一下&#xff0c;因为后来做的项目一直用不上&#xff0c;也就没有注意到 前段时间有网友反馈了一些问题&#xff0c;这…

“奥利”来啦,腾讯Robotics X实验室跑出的“轮滑小子”

6月3日&#xff0c;腾讯Robotics X实验室的新成员——轮腿式机器人Ollie&#xff08;奥利&#xff09;正式亮相&#xff0c;它是机器狗Jamoca、Max和自平衡自行车之后&#xff0c;实验室又一创新成果。轮腿式机器人&#xff08;wheel-legged robot&#xff09;是近年来机器人研…

如何写一篇好的技术博客

在工作过程中&#xff0c;发现对很多东西都一知半解&#xff0c;不是很透澈&#xff0c;到头来很容易模糊&#xff0c;如果有一篇好的技术博客予以总结&#xff0c;一来即使忘记了&#xff0c;回国头来再看&#xff0c;仍然能 够从自己的思路中恢复&#xff1b;二来总结一下&am…

使用react心得

为什么80%的码农都做不了架构师&#xff1f;>>> 在使用react中,总会碰到这样那样的问题,如何解决这些问题,希望能够随着时间的积累,慢慢成熟! 在react中的renden函数里,不能使用setState这个方法,不然会死循环,原因:是因为setState会触发render,而render后又触发se…

将Byte数组转化为String

FCL得很多方法的返回值都是包含字符的Byte数组而不是返回一个String&#xff0c;这样的方法包含在如下的类中&#xff1a; System.Net.Sockets.Socket.Receive System.Net.Sockets.Socket.ReceiveFrom System.Net.Sockets.Socket.BeginReceive System.Net.Sockets.Socket.B…

[题解]UVA10054 The Necklace

链接&#xff1a;http://vjudge.net/problem/viewProblem.action?id18806 描述&#xff1a;给出一堆珠子&#xff0c;每个珠子有两种颜色&#xff0c;有一端颜色相同的珠子可以串在一起&#xff0c;问是否可以把所有珠子串在一起&#xff0c;并求其中一种方案。 思路&#xff…

程序员大厂不一定要进,算法必须要学!收藏89篇精选算法文章

为什么程序员都需要学算法&#xff1f;程序员对算法通常怀有复杂情感&#xff0c;算法很重要是共识&#xff0c;但是否每个程序员都必须学算法是主要的分歧点。很多人觉得像人工智能、数据搜索与挖掘这样高薪的工作才用得上算法&#xff0c;觉得算法深不可测。在面试中&#xf…

专有云到混合云,是云计算的下半场?

查获案件案值达数十亿&#xff0c;为国家挽回近十亿元税款&#xff0c;是海关情报系统在全国应用一年后交出的答卷。 海关情报系统是海关总署与阿里云专有云共同搭建海关大数据云平台后推出的首个应用。 专有云的使命&#xff1a;激发政企大脑潜能 十年前&#xff0c;自己动手D…

C# 2.0 的partial

partial 关键字的作用是将你的 class 分为多个部分&#xff0c;编译器会将多个部分拼到一起去。 public partial class SampleClass ...{ public void MethodA() ...{ } } public partial class SampleClass ...{ public void MethodB() ...{ } } 和 public class Sa…

findbugs:may expose internal representation by ret

2019独角兽企业重金招聘Python工程师标准>>> findbugs&#xff1a;1. *** getXXX() may expose internal representation by returning ***.getXXX 2. *** setXXX(DATE )may expose internal representation by storing an externally mutable object into setXXX *…

AI时代的幕后英雄:谁在生产高质量的AI训练数据?

在AI浪潮的推动下&#xff0c;软件正在朝着更「智能」的方向发展。2017年&#xff0c;特斯拉人工智能部门主管、李飞飞高徒Andrej Karpathy提出了「软件2.0」的概念。 什么是「软件2.0」&#xff1f;其实就是神经网络。 在「软件1.0」时代&#xff0c;程序员用Java、Python、…

Webpack 核心开发者 Sean Larkin 盛赞 Vue

dev.io 近日邀请了 Webpack 核心开发者 Sean Larkin 回答开发者提问&#xff0c;其中几个问提比较有意思&#xff0c;和掘金的小伙伴们分享一下。 先上点前菜&#xff1a; 有一个开发者问 Sean 如何成为一个热门项目的核心作者。Sean 没有一上来就说该做什么&#xff0c;而是先…

设计模式C#描述——单例与多例模式

设计模式C#描述——单例与多例模式 作为对象的创建模式&#xff0c;单例模式确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例。这个类称为单例类。 单例模式有以下特点&#xff1a; 单例类只能有一个实例。 单例类必须自己创建自己的唯一实例。 单…

Nutch插件开发及发布流程

2019独角兽企业重金招聘Python工程师标准>>> 一&#xff0c;插件开发流程&#xff1a; 1&#xff0c;Nutch开发客户端环境搭建 2&#xff0c;plugin的源代码则保存在/src/java/org/apache/nutch/parse/self/ 类实现实例&#xff1a; public class CustomizedIndexin…

网红 AI 高仿坎爷发布说唱情歌,歌迷:堪比真人原声

来源 | Hyper超神经头图 | 下载于视觉中国近日&#xff0c;一个基于 Tacotron2 和 Transformer 实现文字转声音的 AI 应用——Uberduck.AI 破圈了&#xff0c;不少 TikTok 、YouTube 网红博主都在推荐这一神器。YouTube 的网红音乐艺术创意机构 Herr Fuchs 发布了一首新歌&…

设计模式C#描述——抽象工厂模式

设计模式C#描述——抽象工厂模式 阅读此文应先阅读简单工厂模式与工厂方法模式 抽象工厂模式是对象的创建模式&#xff0c;它是工厂方法模式的进一步推广。 假设一个子系统需要一些产品对象&#xff0c;而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责…