用 JavaScript 的匿名函数理解 C# 的委托
我的脑子里有个名词一直在纠结:委托。
顾名思义,委托,把事情托付给他人或机构(办理)。造句诸如:“当事人委托律师出庭辩护”,“我能委托你办一件事吗”。 很明显,委托是个抽象动作(Action),目的具体不详,“出庭辩护”,“办一件事”才是真正要做的事。但C#中委托却让我之前一头雾水,因为这个概念从来未有如此摊开摆上台面。
我确信在以往的 JavaScript 编程中,有类似“委托”这个概念的,比如按钮事件绑定,匿名函数。而网上搜罗有关 C# 委托的言语也大多与函数指针、事件绑定有关。下面将用 JavaScript 与 C# 两种“委托”相对比,用于加深理解。
先看JavaScript中,给按钮定义事件的方法:
<input type="button" value="Hello" id="btn" /> <script type="text/javascript"> //方法一: function SayHello() { alert("Hello world!"); } document.getElementById("btn").attachEvent("onclick", SayHello); //方法二,匿名函数 document.getElementById("btn").attachEvent("onclick", function(){alert("Hello world")}); </script>
方法一是把一个现有的函数引用(类指针)赋值给按钮的事件,而方法二是在给按钮定义事件时,就地定义一个匿名函数。这两种方法其实是一致的,如果把 SayHello 的定义方式换用另外一种方式,将会发现,这一切不过是代码的游戏:
var SayHello = function(){alert("Hello world!")};
方法一与方法二不过是引用上面代码等号的左边与右边而已!
再看看C#:
protected void Page_Load(object sender, EventArgs e) { btn.Click += new System.EventHandler(btn_Click); } void btn_Click(object sender, EventArgs e) { //do sth }
这是众所熟悉的代码,btn 是页面上的一个按钮,+= 表示在原有的基础上续加一个事件,这与IE下的 attachEvent(FF下的 addEventLisener) 同出一辙。System.EventHandler 实际上就是一个委托,通过 ILSpy 查看这个类:
using System; using System.Runtime.InteropServices; namespace System { [ComVisible(true)] [Serializable] public delegate void EventHandler(object sender, EventArgs e); } //构造函数: // System.EventHandler public extern EventHandler(object @object, IntPtr method);
因此,给按钮增加事件,是先创建一个委托,委托再指定需要调用的函数(如btn_Click)。
通过对比两种语言在以上事件定义实例上,C# 非常细致地抽象出这个动作,把委托与事件函数区分开来,这是JavaScript比较模糊的地方(或称为JS的灵活性?)。
再看另外一种场合,JQuery 可以这样遍历数组:
<script type="text/javascript"> function DoSth(obj, i) { /*do sth*/ } $("#box ul li").each(DoSth); //或者这样: $("#box ul li").each(function(obj, i){/*do sth*/}); </script>
以上代码的 $() 函数返回的是元素数组,each() 方法的作用是遍历并处理此数组。那它是怎样做这样的效果呢? 它把另外一个函数(假设为函数DoSth)当作参数,传到 each() 内部,所有处理动作都由函数DoSth来完成,并且默认规定好了接口,函数DoSth只能接收两个参数,第一个是数组中的元素(弱类型),第二个是整型计数器。
把一个函数当地另外一个函数的参数,这样的案例在原生的 JavaScript 还有如: replace(/re/, function($1){}), array.sort(function(x){}),用法一致,不再讨论。
再看看 C#:
protected void Page_Load(object sender, EventArgs e) { List<string> Arr = new List<string>() { "2011年","10月","22日"}; //方法一 Arr.ForEach(delegate(string txt) { Write(txt); }); //方法二 Arr.ForEach(new Action<string>(Write)); //方法三 Arr.ForEach(Write); } private void Write(string txt) { Response.Write(txt); }
上面的代码与 JavaScript 何其相似。方法一就地定义委托,并定义委托的实际内容。方法二采用C#3.0的 Action<T> 委托,它是 delegate 类的泛型重载。方法三是编译器给我们提供的便利,它是方法二的简写。
行文至此,委托的概念顿时明晰,混沌的思维豁然开朗,揭开面纱,原来早已熟悉多时,猛拍脑门,狠掐大腿......
本文最早发布于博客园:https://www.cnblogs.com/xmlnode/archive/2011/10/22/2220890.html,再转帖至此。
蔡大卫,广东揭阳人氏,现居深圳,从事互联网行业,专注程序编码工作20年。目前正在创业。
发布评论