zoukankan      html  css  js  c++  java
  • javascript中文版教程(转自 幸福清扬)

    关键词:迭代模式、foreach、迭代器、IEnumerable、IEnumerator、泛型

    导言:这两天看迭代模式,一边学习,一边联系到.NET Framework的有关设计,小有收获,写下此文以供日后查看。这个关联学习的过程包括以下几个阶段:

    学习迭代模式

     à

    想到了对应.NET中的foreach

     à

    在深入了解foreach的时候碰到了泛型

    à

    然后回归到.NET集合内的迭代,认识了到了“迭代器”的说法

    一、             迭代模式

     

    定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其中内部的表示。

    关于迭代模式的正统解释和示例说明,不想多说,参考《HeadFirst》或《设计模式》。谈谈个人的理解:

           首先,迭代模式出现在对集合遍历的需求上。

    集合(C)有很多形式:数组、ArrayList、HashTable等等,如果要对这些集合的实例变量做遍历,大的方式和流程是一致的,但是具体实现代码不尽相同;

    为了尽量减少代码数量和尽可能的实现逻辑抽象,就有人想出了Iterator(迭代接口),让这些集合都实现这个接口,以方便对接口的统一调用;

    但是如此就违反了OO设计的“职责单一”原则,所以需要另外设置一个类(E)来实现Iterator接口,当然这个接口需要以集合为参数。

    获取某个集合的迭代接口对象,可以放在E内实现,也可以放在C内实现。从用户角度考虑,面向用户的信息量要尽量精简、屏蔽实现细节,所以最好放在C内,这也不怎么违背集合C的职责单一性。

    二、             Foreach

     

    迭代模式不就是实现集合遍历嘛,这让我想到了.NET2.0新增的语法foreach,foreach就是用来对集合实现遍历的,所以我猜想能被foreach的集合可定实现了迭代,所以我马上参看了《C#语言规范》关于foreach的说明:

    5.3.3.16 foreach 语句(因为没有参看上下文,没太看懂,抽时间再看)

    8.8.4 foreach 语句(看懂了,说两句)

     

    《C#语言规范》的相关全文不做全部转载,自行参考。我关心的有几句话:

    首先是foreach的使用限制:在 foreach 语句执行期间,迭代变量表示当前正在为其执行迭代的集合元素。如果嵌入语句试图修改迭代变量(通过赋值或 ++ 和 -- 运算符)或将迭代变量作为 ref 或 out 参数传递,则将发生编译时错误。

    为什么会这样呢?这是我的一个疑问,我后边会解释。

    其次,《C#语言规范》明确说明了什么叫集合:如果类型 C 实现了 System.Collections.IEnumerable 接口,或者能够满足下列(有关实现集合模式的)条件,就称它是集合类型:

    • C 包含一个 public 实例方法,它带有签名 GetEnumerator(),且返回值属于结构类型、类类型或接口类型(后文中用 E 表示该返回值的类型)。
    • E 包含一个 public 实例方法,此方法具有签名 MoveNext() 和返回类型 bool。
      • E 包含一个名为 Current 的 public 实例属性,此属性允许读取当前值。此属性的类型称为该集合类型的元素类型。

     

    我为什么会留意这一点呢,因为规范上说:foreach 语句的表达式的类型必须是集合类型。

    老实说,之前我对foreach的实现原理总有种神秘感,现在了解了其原理之后,手痒痒的厉害,就试着做了个测试:自己定义一个集合,但不实现IEnumerable接口,代码如下:

    using System;

    using System.Collections;

    using System.Collections.Generic;

    using System.Text;

    namespace 迭代

    {

        class Program

        {

            static void Main(string[] args)

            {

                MyIEnumerator mi = new MyIEnumerator();

                foreach (int i in mi)

                {

                    Console.WriteLine(i);

                }

                Console.Read();

            }

        }

        class MyIEnumerator //: IEnumerator

        {

            int i;

            public MyIEnumerator GetEnumerator()

            {

                return this;

            }

            public MyIEnumerator()

            {

                i = -1;

            }

            public object Current

            {

                get { return i; }

            }

            public bool MoveNext()

            {

                if (i < 3)

                {

                    i++;

                    return true;

                }

                else

                {

                    return false;

                }

            }

            //public void Reset()

            //{

            //    i = -1;

            //}

        }

    }

           这段程序测试通过,兴奋!不过兴奋归兴奋,以后实际使用还是继承IEnumerable来的规范一点。

    最后,《规范》给出了foreach的背后扩展形式

    E enumerator = (collection).GetEnumerator();

    try {

       while (enumerator.MoveNext()) {

           ElementType element = (ElementType)enumerator.Current;

           statement;

       }

    }

    finally {

       IDisposable disposable = enumerator as System.IDisposable;

       if (disposable != null) disposable.Dispose();

    }

    并说,enumerator 变量是一个临时变量,它在嵌入 statement 中既是不可访问的,也是不可见的,元素变量在嵌入 statement 中是只读的。

    这倒解释了,前边使用了foreach的限制,那enumerator为什么又是个临时变量呢?这又是个疑问留在后边解释。

    三、           泛型

    可爱的泛型,自从我了解到什么是泛型之后,我就有一种说不的快乐,哈哈,原来泛型这么好使,这里我不打算详细说我对泛型的理解,我对它的喜爱足以让我另立专题,这里我只想说说,我怎么从foreach找到了泛型。

    我们一般新建一个cs文件(类文件)时,VS2005默认就给出了三个引用项

    using System;

    using System.Collections.Generic;

    using System.Text;

    我在认识IEnumerator接口的时候,尝试写这么一句话

    IEnumerator E = list. GetEnumerator(),却发现编辑环境居然不自动提示IEnumerator这个单词,而且我写出来也不变色(是类或接口就应该变成绿色嘛),奇怪了(IEnumerator是声明在System.Collections命名空间内的,我忘了引用,要是出来才奇怪!)

    ,却出来了个IEnumerator<T>的提示,泛型?不了解,决定翻翻书……

           关于泛型,我还想说的是,三个默认的引用文件就有其一个System.Collections.Generic,说明其何等的重要。

           另外一句话,是刚刚看设计模式得到的“使用泛型来确保foreach的类型安全”,深感认同。(不过,原话不是这么说了,原话是说的java的for/in语法,我自己翻译了一下)

    四、.NET中的迭代器

           好了,再回到迭代模式。.NET Framework 已经将迭代模式做进了框架内所有定义的集合类型内,MSDN中称这个所有集合都能返回的接口IEnumerator为迭代器(和java的Iterator对应)。从单一职责的角度考虑,GetEnumerator()方法所返回的接口实例,应该是实现集合遍历的另外一个类,这个类呢?我通过Reflector查看源码没有看到,上网查了查,原来微软将迭代器做进了编译器,真绝!不仅如此,为了方便用户实现自己的迭代器,微软还为迭代器的快捷生成设置了 yield return 这样的语法,如下例。关于yield是什么,请自行参看MSDN帮助,我的白话理解是:能够允许连续返回多个值,而且返回的这多个值就构成了一个可以遍历的迭代器,这是编译器自己干的。

    public class Persons : IEnumerable

    {

        string[] m_Names;

        public Persons(params string[] Names)

        {

            m_Names = new string[Names.Length];

            Names.CopyTo(m_Names,0);

        }

        public IEnumerator GetEnumerator()

        {

            foreach (string s in m_Names)

            {

                yield return s;

            }

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            Persons arrPersons = new Persons("Michel","Christine","Mathieu","Julien");

            foreach (string s in arrPersons)

            {

                Console.WriteLine(s);

            }

            Console.ReadLine();

        }

    }

    最后,顺便解释一下,foreach的约束问题(还记得吗?)。正因为迭代器是编译器默认生成的,而且是用户不可见的,而且是临时的,所以尝试对它的修改和引用注定是不被允许的,当然了,遍历看一看,读取一下总是可以的。

    学习参考

    .NET设计模式(18):迭代器模式(Iterator Pattern)

    http://www.cnblogs.com/Terrylee/archive/2006/09/16/iterator_pattern.html

    C# 2.0 新特性之迭代器, Yield Return

    http://montaque.cnblogs.com/archive/2005/04/21/142844.html

    疑问:yield到底是怎么运作的?  

    http://blog.csdn.net/kangfucat/archive/2007/08/21/1752239.aspx

    生活TMD需要激情,做事需冷静,说话需冷静!

    遇事记着:办法总比困难多,困难和问题说不定就是机遇和转折!

    历史证明:哪个环节没照顾到,哪个环节就会出问题!能自己来,就不要让别人来。

  • 相关阅读:
    Linux
    Linux
    Linux
    Linux
    Linux
    Python
    Linux
    Python
    MySQL
    Python
  • 原文地址:https://www.cnblogs.com/zhcw/p/2795566.html
Copyright © 2011-2022 走看看