About闭包

using System;
using System.Collections.Generic;
using System.Collections;

class Program
{
    static void Main(string[] args)
    {
        //第一个例子
        //int counter = 0;
        //IEnumerable<int> values = Utilities.Generate(10, () => counter++);

        //Console.WriteLine("Current Counter: {0}", counter);
        //foreach (int num in values)
        //    Console.WriteLine(num);

        //Console.WriteLine("Current Counter: {0}", counter);

        //foreach (int num in values)
        //{
        //    Console.WriteLine(num);
        //}

        //Console.WriteLine("Current Counter: {0}", counter);
        //IEnumerator<int> obj = GetEnumerator();
        //foreach (var item in obj)
        //{
        //    Console.WriteLine(item);   
        //}        

        //第二个例子
        A a=new A();
        IEnumerable<int> arr = a.Fun();
        foreach (var tem in arr)
        {
            Console.WriteLine(tem);
        }
        foreach (var tem in arr)
        {
            Console.WriteLine(tem);
        }

        //第三个例子
        //foreach (var item in a)
        //{
        //    Console.WriteLine(item);
        //}
        //Console.WriteLine("-------------------------------");
        //foreach (var item in a)
        //{
        //    Console.WriteLine(item);
        //}
    }
}

class A : IEnumerable<int>
{
    int count = 0;
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < 5; i++)
            yield return count++;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        for (int i = 0; i < 5; i++)
            yield return count++;

    }

    public IEnumerable<int> Fun()
    {
        for (int i = 0; i < 5; i++)
            yield return count++;
    }
}

class B : IEnumerable<int>
{
    #region IEnumerable<int> 成员

    public IEnumerator<int> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IEnumerable 成员

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}


public static class Utilities
{
    public static IEnumerable<T> Generate<T>(int num, Func<T> generator)
    {
        int index = 0;
        while (index++ < num)
            yield return generator();
    }
}

 

闭包的实现原理:

 

在.NET 1.x时我们可能会写这样的代码:1

public static void TestRequest(string url)
{
    WebRequest request = HttpWebRequest.Create(url);
    object[] context = new object[] { url, request };
    request.BeginGetResponse(TestAsyncCallback, context);
}

public static void TestAsyncCallback(IAsyncResult ar)
{
    object[] context = (object[])ar.AsyncState;
    string url = (string)context[0];
    WebRequest request = (WebRequest)context[1];

    using (WebResponse response = request.EndGetResponse(ar))
    {
        Console.WriteLine("{0}: {1}", url, response.ContentLength);
    }
}我们需要自己维护一个object[]作为callback的参数,这样不仅是弱类型还很麻烦。好在.NET 2.x引入了匿名方法:

public static void TestRequest(string url)
{
    WebRequest request = HttpWebRequest.Create(url);
    request.BeginGetResponse(delegate(IAsyncResult ar)
    {
        using (WebResponse response = request.EndGetResponse(ar))
        {
            Console.WriteLine("{0}: {1}", url, response.ContentLength);
        }
    },
    null);
}很方便吧!2 新定义的delegate可以直接用TestRequest里的变量,就在于生成了一个闭包。

闭包(Closure)是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。3

C#中闭包的实现
从上边看来闭包与匿名方法有点关系,但.NET中本并没有什么匿名方法,我们还是用Reflector看下编译器到底生成了什么样的代码。这里注意要修改下Reflector的设置,View->Options->Disassembler->Optimization改为.Net 1.0 。

public static void TestRequest(string url)
{
    <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
    CS$<>8__locals2.url = url;
    CS$<>8__locals2.request = WebRequest.Create(CS$<>8__locals2.url);
    CS$<>8__locals2.request.BeginGetResponse(new AsyncCallback(CS$<>8__locals2.<TestRequest>b__0), null);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public WebRequest request;
    public string url;

    // Methods
    public void b__0(IAsyncResult ar)
    {
        using (WebResponse response = this.request.EndGetResponse(ar))
        {
            Console.WriteLine("{0}: {1}", this.url, response.ContentLength);
        }
    }
}

可见编译器至少做了两件事

生成了一个类,包括匿名方法的方法体和所有引用的自由变量
调用匿名方法的方法里的相关变量都被替换为生成类的字段,相关方法被替换为生成类的方法

 

 

 

 

posted @ 2010-07-29 09:40  再快一点  阅读(194)  评论(0编辑  收藏  举报