`
cdragon
  • 浏览: 76940 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

关于“匿名方法”与“lambda操作符”的实现

阅读更多

在<关于“回调”的实现>一文中,我们探讨了使用委托实现回调。但对于回调的原型来讲,我们感到其使用上的繁琐,本文我们使用“匿名”方法来对其简化。

 

我们首先回顾一下,实现回调的主要步骤:

1、定义委托

2、定义调用者

3、定义执行体函数

 

我们来看下,通过匿名方法--对回调一文中的完整示例进行语法简化后的--程序如下:

using System;
using System.Collections;

class DBConnection
{
    protected static int NextConnectionNbr = 1;

    protected string connectionName;
    public string ConnectionName
    {
        get
        {
            return connectionName;
        }
    }

    public DBConnection()
    {
        connectionName = "Database Connection " 
            + DBConnection.NextConnectionNbr++;
    }
}

class DBManager
{
    protected ArrayList activeConnections;
    public DBManager()
    {
        activeConnections = new ArrayList();
        for (int i = 1; i < 6; i++)
        {
            activeConnections.Add(new DBConnection());
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
};

class InstanceDelegate
{
    /*不再需要函数定义
    public static void PrintConnections(DBConnection connection)
    {
        Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
            connection.ConnectionName);
    }*/

    public static void Main()
    {
        DBManager dbManager = new DBManager();

        Console.WriteLine("[Main] Instantiating the " +
            "delegate method");
        /* 
        DBManager.EnumConnectionsCallback _printConnections =
             new DBManager.EnumConnectionsCallback(PrintConnections);*/

        //替换如下
        DBManager.EnumConnectionsCallback _printConnections =
             delegate(DBConnection connection){
               Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
               connection.ConnectionName);
         }

        Console.WriteLine("[Main] Calling EnumConnections " +
            "- passing the delegate");
          dbManager.EnumConnections(_printConnections);

        Console.ReadLine();
    }
};

 

小结:

我们看到,于委托中忽略了函数名称的函数代码块,被称作anonymous method,即匿名方法。既然委托是同构函数的归类,就没有必要再为函数声明进行定义,所以,匿名方法凸显了委托的意义、简化了委托的使用。--匿名方法不需要使用函数名的原因是编译器会自动为delegate关键字处理函数名,而programmer并不需要这个函数名,而且有无意义不是很大。

 

更进一步,既然委托可以被“看作”对同类函数的语法抽象,我们可以用匿名方法忽略函数定义的header,那么我们当然可以忽略函数的名称而只保留参数,这就是“lambda语句”在委托中的应用。见实例:

using System;
using System.Collections;

class DBConnection
{
    protected static int NextConnectionNbr = 1;

    protected string connectionName;
    public string ConnectionName
    {
        get
        {
            return connectionName;
        }
    }

    public DBConnection()
    {
        connectionName = "Database Connection " 
            + DBConnection.NextConnectionNbr++;
    }
}

class DBManager
{
    protected ArrayList activeConnections;
    public DBManager()
    {
        activeConnections = new ArrayList();
        for (int i = 1; i < 6; i++)
        {
            activeConnections.Add(new DBConnection());
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
};

class InstanceDelegate
{
    public static void Main()
    {
        DBManager dbManager = new DBManager();

        Console.WriteLine("[Main] Instantiating the " +
            "delegate method");

/*        DBManager.EnumConnectionsCallback _printConnections =
             delegate(DBConnection connection){
               Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
               connection.ConnectionName);*/
          //替换如下
        DBManager.EnumConnectionsCallback _printConnections =
             connection=>{
               Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
               connection.ConnectionName);
         }

        Console.WriteLine("[Main] Calling EnumConnections " +
            "- passing the delegate");
          dbManager.EnumConnections(_printConnections);

        Console.ReadLine();
    }
};

 

小结:lambda的引入,产生了变量超出其范围而与委托的生存周期同步这一“现象”,这里我们暂称这种变量为外部变量(outer variable),这时系统不再视变量为固定的而是可移动的,但可使用fixed关键词来控制(fixed关键词的使用见后)。

 

最后,我们通过引入泛型来对该程序进行性能优化,示例如下:

 

using System;
using System.Collections;
using System.Collections.Generic;
class DBConnection
{
    protected static int NextConnectionNbr = 1;

    protected string connectionName;
    public string ConnectionName
    {
        get
        {
            return connectionName;
        }
    }

    public DBConnection()
    {
        connectionName = "Database Connection " 
            + DBConnection.NextConnectionNbr++;
    }
}

class DBManager<T>
{
    protected ArrayList activeConnections;
    public DBManager()
    {
        activeConnections = new ArrayList();
        for (int i = 1; i < 6; i++)
        {
            activeConnections.Add(new DBConnection());
        }
    }

    public delegate void EnumConnectionsCallback(T connection);
    public void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (T connection in activeConnections)
        {
            callback(connection);
        }
    }
};

class InstanceDelegate
{
    public static void Main()
    {
        DBManager dbManager = new DBManager();

        Console.WriteLine("[Main] Instantiating the " +
            "delegate method");

        DBManager.EnumConnectionsCallback _printConnections =
             connection=>{
               Console.WriteLine("[InstanceDelegate.PrintConnections] {0}",
               connection.ConnectionName);
         }

        Console.WriteLine("[Main] Calling EnumConnections " +
            "- passing the delegate");
          dbManager.EnumConnections(_printConnections);

        Console.ReadLine();
    }
};

小结:通过对DBManger引进泛型,使得负责数据库连接的DBConnection与委托部分的程序相分离。 这就是所谓的“泛型委托”。

 

总结:由此,我们通过“委托推理”(delegate inference)、“匿名方法”(anonymous method)、“lambda表达式(type inference)”,基本完成了回调一文中回调原型的简化。 效果就是取得客户端代码最优化,这也意味着良好的易读性。而lambda最重要的意义在于实现了算法复用(algorithm reusing),MS建议使用lambda取代匿名方法。

 

附,fixed keyword的使用

fixed 语句禁止垃圾回收器重定位可移动的变量。fixed 语句只能出现在不安全的上下文中。Fixed 还可用于创建固定大小的缓冲区。

fixed 语句设置指向托管变量的指针并在 statement 执行期间“钉住”该变量。如果没有 fixed 语句,则指向可移动托管变量的指针的作用很小,因为垃圾回收可能不可预知地重定位变量。C# 编译器只允许在 fixed 语句中分配指向托管变量的指针。

// assume class Point { public int x, y; }
// pt is a managed variable, subject to garbage collection.
Point pt = new Point();
// Using fixed allows the address of pt members to be
// taken, and "pins" pt so it isn't relocated.
fixed ( int* p = &pt.x )
{
    *p = 1; 
}

可以用数组或字符串的地址初始化指针:

fixed (int* p = arr) ...  // equivalent to p = &arr[0]
har* p = str) ... // equivalent to p = &str[0]

只要指针的类型相同,就可以初始化多个指针:

fixed (byte* ps = srcarray, pd = dstarray) {...}

要初始化不同类型的指针,只需嵌套 fixed 语句:

fixed (int* p1 = &p.x)
{
d (double* p2 = &array[5])
    {
// Do something with p1 and p2.
    }
}

执行完语句中的代码后,任何固定变量都被解除固定并受垃圾回收的制约。因此,不要指向 fixed 语句之外的那些变量。

无法修改在 fixed 语句中初始化的指针。

在不安全模式中,可以在堆栈上分配内存。堆栈不受垃圾回收的制约,因此不需要被锁定。

// statements_fixed.cs
// compile with: /unsafe
using System;
 
class Point
{ 
    public int x, y; 
}
 
class FixedTest 
{
    // Unsafe method: takes a pointer to an int.
    unsafe static void SquarePtrParam (int* p) 
    {
        *p *= *p;
    }
 
    unsafe static void Main() 
    {
        Point pt = new Point();
        pt.x = 5;
        pt.y = 6;
        // Pin pt in place:
        fixed (int* p = &pt.x) 
        {
            SquarePtrParam (p);
        }
        // pt now unpinned
        Console.WriteLine ("{0} {1}", pt.x, pt.y);
    }
}

结果为:

25 6
分享到:
评论

相关推荐

    C++11 Lambda表达汇总总结

     可以看到,Lambda主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable或exception声明、-&gt;返回值类型、{函数体}。下面分别进行介绍。  一、[函数对象参数],标识一个Lambda的开始,这部分必须...

    C#5.0本质论第四版(因文件较大传的是百度网盘地址)

    9.2.3 赋值与二元操作符的结合 266 9.2.4 条件逻辑操作符 266 9.2.5 一元操作符 267 9.2.6 转换操作符 268 9.2.7 转换操作符的规范 269 9.3 引用其他程序集 269 9.3.1 更改程序集目标 ...

    Python核心编程(第二版).pdf (压缩包分2部分,第二部分)

     6.17.2 元组类型操作符和内建函数、内建方法   6.18 元组的特殊特性   6.18.1 不可变性给元组带来了什么影响   6.18.2 元组也不是那么“不可变”   6.18.3 默认集合类型   6.18.4 单元素元组   ...

    Python核心编程(第二版).pdf (压缩包分2部分,第一部分)

     6.17.2 元组类型操作符和内建函数、内建方法   6.18 元组的特殊特性   6.18.1 不可变性给元组带来了什么影响   6.18.2 元组也不是那么“不可变”   6.18.3 默认集合类型   6.18.4 单元素元组   ...

    Python核心编程第二版(ok)

     6.17.2 元组类型操作符和内建函数.c内建方法   6.18 元组的特殊特性   6.18.1 不可变性给元组带来了什么影响   6.18.2 元组也不是那么“不可变”   6.18.3 默认集合类型   6.18.4 单元素元组   ...

    LINQ 实战 1/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 3/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 4/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 2/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 7/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 11/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 5/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 8/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 6/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 9/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    LINQ 实战 10/11

    3.4.3 标准查询操作符与查询表达式的关系 75 3.4.4 限制 76 3.5 表达式树 78 3.5.1 Lambda表达式回顾 78 3.5.2 表达式树是什么 78 3.5.3 IQueryable,另一种实现延迟查询执行的方法 81 3.6 LINQ的程序...

    Python核心编程第二版

     6.17.2 元组类型操作符和内建函数、内建方法   6.18 元组的特殊特性   6.18.1 不可变性给元组带来了什么影响   6.18.2 元组也不是那么“不可变”   6.18.3 默认集合类型   6.18.4 单元素元组   ...

    Visual C#2010 从入门到精通(Visual.C#.2010.Step.By.Step).完整去密码锁定版 I部分

    21.7.2 实现用户自定义的转换操作符 398 21.7.3 再论创建对称操作符 399 21.7.4 添加隐式转换操作符 399 第21章快速参考 402 第iv部分 操纵windows应用程序 第22章 windows presentation foundation概述 405 ...

    【05-面向对象(下)】

    基本数据类型的包装类 •八大数据类型的包装类分别为:Byte、Short、Integer、Long、Character、 Float、Double、Boolean。...函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该...

    java8源码-StartFromZero-JavaCore:java编程基础

    操作符右侧:表达式执行的功能,称为Lambda体。 接口中仅包含一个抽象方法的接口称为函数式接口,可以使用@FunctionalInterface注解修饰,编译器可检验函数式接口的合法性。Lambda表达式需要只能用在函数式接口中...

Global site tag (gtag.js) - Google Analytics