`
cdragon
  • 浏览: 79203 次
  • 性别: 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
分享到:
评论

相关推荐

    初步理解委托、事件、匿名方法和Lambda1

    事件源通过`+=`操作符订阅事件处理程序,将委托实例与事件处理方法关联起来。例如,对于一个按钮点击事件: ```csharp this.button1.Click += new System.EventHandler(this.button1_Click); ``` 在这段代码中,`...

    进度条.Lambda表达式.匿名方法.委托.类.集合.初始化

    "匿名方法"与Lambda表达式类似,但不使用lambda操作符。它是在没有定义名称的情况下创建的方法,通常作为参数传递给其他方法。例如,`delegate(int x, int y) { return x + y; }` 是一个匿名方法,实现了加法操作。 ...

    委托匿名方法LAMDA表达式

    Lambda表达式使用“=&gt;”操作符,左边是输入参数(如果有的话),右边是表达式或语句块。Lambda表达式可以与委托紧密结合,尤其在LINQ查询中广泛使用。 Lambda表达式的基本语法如下: ```csharp (input parameters) ...

    Lambda教程

    Lambda表达式不能作为`is`和`as`操作符的左侧,也不能在不安全的上下文中使用。 7. **与匿名方法的比较** Lambda表达式相比于匿名方法更简洁,因为它们不需要`delegate`关键字和花括号(对于单行Lambda)。同时,...

    C#lambda表达式的使用

    所有的 Lambda 表达式都使用操作符“=&gt;“,表示“goes to (转变为)”。 Lambda 表达式简介 Lambda 表达式是一个包含若干表达式和语句的匿名函数。可以被用作创建委托对象或表达式树类型。所有的 Lambda 表达式都...

    lambda表达式学习demo

    - Lambda 表达式必须与函数式接口关联,因为它们代表了接口的一个实现。函数式接口是指只有一个抽象方法的接口,例如 `java.util.function.Function` 和 `java.util.Comparator`。 3. **Lambda 实例** - 对于...

    Lambda表达式--凌尘.pptx

    Lambda 表达式的概念包含两个主要部分:参数列表(左侧)和函数体(右侧),两者之间由 Lambda 操作符 "-&gt;" 分隔。参数列表可以为空,也可以包含一个或多个参数,根据情况可以省略括号。函数体可以是单个表达式或一...

    java lambda 表达式(语言篇和类库篇)

    4. **三元操作符与Lambda**: Lambda表达式可以替代简单的条件表达式,比如 `(a, b) -&gt; a &gt; b ? a : b` 可以用于比较两个数的大小。 5. **并行流与Lambda**: Java 8的Stream API支持并行处理,通过`parallel...

    Java Lambda表达式

    5. 匿名内部类与Lambda的对比:在Java 8之前,我们通常通过匿名内部类来实现函数式接口。Lambda表达式则提供了更简洁的写法,减少了代码量,提升了可读性。 6. 方法引用和构造器引用:Lambda表达式还可以直接引用已...

    lambda-change-java.rar_java lambda_lambda

    `java.util.stream.Stream` API提供了丰富的操作符,如`map`, `filter`, `reduce`, `forEach`等,这些操作符可以配合Lambda表达式处理集合,实现数据流的处理。例如: ```java List&lt;String&gt; names = Arrays.asList...

    Lambda表达式与LINQ.rar

    在提供的"PPTX"文件"Lambda表达式与LINQ.pptx"中,可能会详细解释Lambda表达式的语法、用法和实际示例,以及如何在C++中实现类似LINQ的查询方式。通过学习这个文件,你可以进一步加深对这两个主题的理解,并将它们...

    C++ Lambda表达式详解

    例如,使用Lambda表达式可以实现匿名函数、函数对象、闭包等高级编程概念。 在实际应用中,Lambda表达式可以用来实现各种函数式编程的需求,如图形处理、数据分析、算法实现等等。同时,Lambda表达式也可以与STL算...

    day07_线程池、lambda表达式-每日作业卷1

    答案:可以使用 Lambda 表达式来实现 Director 接口的抽象方法 makeMovie。 10. 请使用 Lambda 表达式在 Test 中完成调用 Calculator 接口。 答案:可以使用 Lambda 表达式来实现 Calculator 接口的抽象方法 calc...

    使用Lambda表达式查找指定字符

    Lambda表达式的应用远远不止这些,它们可以与LINQ的各种操作符结合,如`Where`, `SelectMany`, `Any`, `All`, `Count`, `Max`, `Min`等,实现复杂的数据过滤、转换和聚合。同时,Lambda表达式也是C#异步编程的重要...

    lambda.rar stream

    Lambda表达式可以被看作是匿名函数,即没有名字的函数,它能够简洁地表示只包含单个抽象方法的接口实例。在"stream"的概念下,Lambda表达式与Java Stream API结合,为开发者提供了强大的数据处理能力。 "Stream"是...

    Python库 | lambdachain-0.1.tar.gz

    2. **可定制化操作**:库提供了自定义操作符的功能,用户可以定义自己的操作符,与现有的Lambda函数结合使用,增强了代码的灵活性。 3. **函数组合**:允许将多个Lambda函数合并为一个,减少代码重复,提高代码复用...

    关于委托的实现

    事件通常使用私有的多播委托来实现,而`+=`和`-=`操作符用于订阅和取消订阅事件: ```csharp public event MyDelegate MyEvent; public void RaiseEvent() { if (MyEvent != null) { MyEvent(10, 20); // 调用...

    Java Lambda表达式:简化代码与增强表达力的利器

    Lambda表达式是一种简洁地表示匿名函数的方法,即一个无需名字的函数。它允许开发者将函数作为程序的一部分来使用,这种能力在函数式编程语言中十分常见。在Java 8中,Lambda表达式可以被赋值给变量、作为参数传递给...

Global site tag (gtag.js) - Google Analytics