Bowen Huang

学习,进步!记录学习的过程,分享知识。

导航

匿名函数和扩展函数

Posted on 2012-08-02 00:08  Bowen Huang  阅读(1550)  评论(1编辑  收藏  举报

注:以下文章仅为学习笔记,很大部分来自于他人博客或资料,我会相应注明,仅为学习。

 

一、匿名函数

  一下大部分来源于:http://blog.csdn.net/xiongwjw/article/details/7248630

  该笔记(匿名函数)没有详细例子,实例详见源博客

  匿名函数与委托有着密切的关系。

  1、为什么要用匿名方法,什么时候用?

    当用常规的委托调用方法时使代码很混乱或者不简洁的时候,可以使用匿名方法(内联代码块定义)。什么意思呢,我们用委托调用方法时,是根据方法名调用的,当需要待用的方法很多时,我们创建很多的方法,但是很多方法又不常用,现在我们可以用匿名方法代替。

    例如,我直接用我上篇博客“委托”中的例子(根据学生的属性排序)来讲,我们可以将委托实现的调用改为如下匿名方法的调用:

//匿名方法实现
Sort(s, delegate(Student s1, Student s2)//根据id排序
{
       return s1.id > s2.id;
});
//Sort(s, delegate(Student s1, Student s2)//根据age排序
//{
      //    return s1.age > s2.age;
//});

    匿名方法允许我们定义委托对象可以接受的代码块。这个功能省去我们创建委托时想要传递给一个委托的小型代码块的一个额外的步骤。它也消除了类代码中小型方法的混乱。

  2、匿名方法的静态数据成员的用法

    匿名方法总是以一个delegate关键字开始,后面跟着用在方法和方法体(the method body)本身中的参数。正如从上面示例中所见,用户不需要确定匿名方法的返回类型。它(译注:指返回类型)由方法体中的return语句推断而来。.NET CLR不能执行像匿名方法一样的自由流(free flowing)代码块。CLR要求:它执行的每个方法是一个类型的一部分,并且应该是一个静态(static)方法或实例(instance)方法(译注:若一个方法声明中含有 static 修饰符,则称该方法为静态方法。若其中没有 static 修饰符时,则称该方法为实例方法。静态方法不对特定实例进行操作,在静态方法中引用 this 是编译时错误。实例方法对类的某个给定的实例进行操作,而且可以用 this来访问该实例)。因此当你在一个类的代码中写匿名方法并编译这个代码时,C#编译器默默地在你定义匿名方法的相同的类中创建了一个静态或实例方法。所以匿名方法只是一个在类中定义你自己方法以传递到委托(委托处理器/事件处理器)的方便的语法。

       当你编译上面的示例时,C#编译器在类''Program''内部即我们定义匿名方法的地方创建了两个private static方法。它此时用这些static方法的地址取代了匿名方法。编译器决定如何创建静态方法或实例方法取决于匿名方法被定义的类中的静态或实例数据成员的用法。在我们的示例中,我们没有用到任何类''Program''的数据成员,因为调用一个静态方法而不是一个实例方法将是高效的,因此C#编译器创建一个static方法来封装我们的匿名方法的代码。

  3、匿名方法的局部变量用法

    从根本上说,C#创建了private方法来包装匿名方法。同时这些方法的签名与它们被分配到的委托相匹配。 

    在让我们进入匿名方法的高级话题。一个匿名方法有封装在其方法体中使用了的环境变量的值的能力。这个封装应用于匿名方法被定义的方法中的所有局部变量。当C#编译器在一个匿名方法的方法体中识别出用到一个局部变量,它就会做如下事情:

    1. 创建一个新的private类作为匿名方法被定义的类的一个内部类。
    2. 在新类(译注:即内部类)中创建一个公共数据成员,使用与用在匿名方法体中的局部变量相同的类型和名称。
    3. 在包装匿名方法的新类中创建一个public实例方法。
    4. 用新类中的声明替代局部变量的声明。创建该新类的一个实例代替局部变量的声明。
    5. 用新类实例的数据成员替代在匿名方法体内部和外部使用的局部变量。
    6. 用在新类中定义的实例方法的地址取代匿名方法的定义。

  4、总结

    匿名方法是C#2.0语言增加的一个非常有用和强大的功能。除了介绍的一些对委托声明和用法上的语法改进,Microsoft已在使匿名方法代码自然融入所包含的方法体方面获得很大进展,包括访问在包含(匿名方法)的方法定义的作用域中的局部变量。

 

二、扩展函数

  以下内容大部分来源于:http://www.cnblogs.com/sunrack/articles/1073759.html

             http://blog.csdn.net/hu8hong9/article/details/1856026

  1、在什么情况下使用

    使用扩展函数,可以为无法修改源代码的对象添加新的方法,或者强制让对象支持某些方法,这些方法看起来就是对象本来就有的功能。当我们在对类A的进行操作时,发现类A中没有某些方法,我们又不能对类A直接修改,但是我们又要用到该方法时,可以用扩展函数的功能新建一个类,并且创建要使用的方法。

   2、使用方法:

    声明扩展函数:扩展函数使用特定的关键字this作为修饰符应用于函数的第一个参数来声明。扩展函数只能声明在static的类中

    导入扩展函数:扩展函数通过using-namespace-directives 导入。另外导入的有,该namespace所包含的类型,using-namespace-directive 导入所有的扩展函数在该namespace中的所有static类。被导入的扩展函数有效地作为在某些类型上的额外函数,这是通过它们的第一个关键字以及比常规的实例函数更低的优先级所给与的。

    扩展函数的调用:如果正常的调用过程中没有发现适用的实例函数(尤其如果对此调用的候选函数为空的情况下),编译器将做出尝试将构造过程作为扩展函数来调用。

  3、限制条件

    1、必须在static class 中定义,所以必须使用static修饰
     2、需要在(只需要在)第一个参数前面使用this修饰
     3、扩展函数可以通过对象实例调用,也可以使用定义该函数的静态类直接调用

  4、简单的例子

    新建一个类:

public static class E {
        public static string F(this object obj, int i)//扩展函数
        {
            return "E:"+i.ToString();
        }

        public static string F(this object obj, string s)
        {
            return "E:" + s;
        }
    }
    class A {//类A中没有方法
    }
    class B {
        public string F(int i)
        {
            return "B:" + i.ToString();
        }
    }
    class C {
        public string F(object obj)
        {
            return "C:" + obj.ToString();
        }
    }

新建一个aspx文件。

<asp:Button ID="Button1" runat="server" Text="测试扩展函数" onclick="Button1_Click" />
        <br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>

添加相应函数如下:

//B的函数优先于前面的扩展函数,并且C的函数优先于所有的扩展函数。
        protected void Button1_Click(object sender, EventArgs e)
        {
            A a = new A();             //A中没有F()函数,将调用扩展函数
            Label1.Text=a.F(1);                         // E.F(object, int)
            Label1.Text +="<br/>"+ a.F("hello");     // E.F(object, string)
            
            B b = new B();             //B中有F(int),则调用此,没有对string的操作,调用扩展函数。
            Label1.Text += "<br/>" + b.F(1);              // B.F(int)
            Label1.Text += "<br/>" + b.F("hello");     // E.F(object, string)

            C c=new C();
            Label1.Text += "<br/>" + c.F(1);              // C.F(object)
            Label1.Text += "<br/>" + c.F("hello");     // C.F(object)
        }

 运行结果如下:

  5、使用范围

    扩展函数本质上是在被扩展的对象实例上可以调用的静态函数,不是继承,所以不同于普通的成员函数,扩展函数不能直接访问被扩展对象的成员。只能通过该对象的实例来访问。

  6、扩展接口类型

    必须给出方法的实现

     扩展接口后,显然不能直接在接口上调用这些扩展函数,只能理解为,所有继承该接口的对象新增加了这些扩展函数功能