关于“匿名方法”与“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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值