• 用 JavaScript 的匿名函数理解 C# 的委托


    我的脑子里有个名词一直在纠结:委托。

    顾名思义,委托,把事情托付给他人或机构(办理)。造句诸如:“当事人委托律师出庭辩护”,“我能委托你办一件事吗”。 很明显,委托是个抽象动作(Action),目的具体不详,“出庭辩护”,“办一件事”才是真正要做的事。但C#中委托却让我之前一头雾水,因为这个概念从来未有如此摊开摆上台面。

    我确信在以往的 JavaScript 编程中,有类似“委托”这个概念的,比如按钮事件绑定,匿名函数。而网上搜罗有关 C# 委托的言语也大多与函数指针、事件绑定有关。下面将用 JavaScript 与 C# 两种“委托”相对比,用于加深理解。

    先看JavaScript中,给按钮定义事件的方法:

     1 <input type="button" value="Hello" id="btn" />
    2 <script type="text/javascript">
    3 //方法一:
    4 function SayHello()
    5 {
    6 alert("Hello world!");
    7 }
    8 document.getElementById("btn").attachEvent("onclick", SayHello);
    9
    10 //方法二,匿名函数
    11 document.getElementById("btn").attachEvent("onclick", function(){alert("Hello world")});
    12 </script>

    方法一是把一个现有的函数引用(类指针)赋值给按钮的事件,而方法二是在给按钮定义事件时,就地定义一个匿名函数。这两种方法其实是一致的,如果把 SayHello 的定义方式换用另外一种方式,将会发现,这一切不过是代码的游戏:

    var SayHello = function(){alert("Hello world!")};

    方法一与方法二不过是引用上面代码等号的左边与右边而已!

    再看看C#:

    1 protected void Page_Load(object sender, EventArgs e)
    2 {
    3 btn.Click += new System.EventHandler(btn_Click);
    4 }
    5
    6 void btn_Click(object sender, EventArgs e)
    7 {
    8 //do sth
    9 }

    这是众所熟悉的代码,btn 是页面上的一个按钮,+= 表示在原有的基础上续加一个事件,这与IE下的 attachEvent(FF下的 addEventLisener) 同出一辙。System.EventHandler 实际上就是一个委托,通过 ILSpy 查看这个类:

     1 using System;
    2 using System.Runtime.InteropServices;
    3 namespace System
    4 {
    5 [ComVisible(true)]
    6 [Serializable]
    7 public delegate void EventHandler(object sender, EventArgs e);
    8 }
    9
    10 //构造函数:
    11 // System.EventHandler
    12 public extern EventHandler(object @object, IntPtr method);

    因此,给按钮增加事件,是先创建一个委托,委托再指定需要调用的函数(如btn_Click)。

    通过对比两种语言在以上事件定义实例上,C# 非常细致地抽象出这个动作,把委托与事件函数区分开来,这是JavaScript比较模糊的地方(或称为JS的灵活性?)。

    再看另外一种场合,JQuery 可以这样遍历数组:

     1 <script type="text/javascript">
    2 function DoSth(obj, i)
    3 {
    4 /*do sth*/
    5 }
    6 $("#box ul li").each(DoSth);
    7
    8 //或者这样:
    9 $("#box ul li").each(function(obj, i){/*do sth*/});
    10 </script>

    以上代码的 $() 函数返回的是元素数组,each() 方法的作用是遍历并处理此数组。那它是怎样做这样的效果呢? 它把另外一个函数(假设为函数DoSth)当作参数,传到 each() 内部,所有处理动作都由函数DoSth来完成,并且默认规定好了接口,函数DoSth只能接收两个参数,第一个是数组中的元素(弱类型),第二个是整型计数器。

    把一个函数当地另外一个函数的参数,这样的案例在原生的 JavaScript 还有如: replace(/re/, function($1){}), array.sort(function(x){}),用法一致,不再讨论。

    再看看 C#:

     1 protected void Page_Load(object sender, EventArgs e)
    2 {
    3 List<string> Arr = new List<string>() { "2011年","10月","22日"};
    4 //方法一
    5 Arr.ForEach(delegate(string txt)
    6 {
    7 Write(txt);
    8 });
    9
    10 //方法二
    11 Arr.ForEach(new Action<string>(Write));
    12
    13 //方法三
    14 Arr.ForEach(Write);
    15 }
    16
    17
    18 private void Write(string txt)
    19 {
    20 Response.Write(txt);
    21 }

    上面的代码与 JavaScript 何其相似。方法一就地定义委托,并定义委托的实际内容。方法二采用C#3.0的 Action<T> 委托,它是 delegate 类的泛型重载。方法三是编译器给我们提供的便利,它是方法二的简写。

    行文至此,委托的概念顿时明晰,混沌的思维豁然开朗,揭开面纱,原来早已熟悉多时,猛拍脑门,狠掐大腿......


  • 相关阅读:
    Eclipse SVN插件设置
    经典语录-每日积累-05
    Shell基础语法,运算符,循环和判断语句和设置启动参数
    iOS-Jenkins自动化打包集成
    App版本升级相关
    Java-数组和集合简单使用
    Java-内部类简单使用
    Callkit被拒
    Java-Finalize(GC)和类与类和接口之间的关系
    经典语录-每日积累-04
  • 原文地址:https://www.cnblogs.com/xmlnode/p/2220890.html
Copyright © 2020-2023  润新知