C#学习:集合、迭代、泛型(1)

一、System.Collections名称空间下几个接口表征着集合的功能:

1、IEnumerable:表征着迭代功能

public interface IEnumerable

{

   

    IEnumerator GetEnumerator();

}



public interface IEnumerator

{

    bool MoveNext();

    object Current { get; }

    void Reset();

}

注意,IEnumerator也属于System.Collections空间。

 2、其它诸如:ICollection IList IDictionary 都表征着相应的集合功能。它们的代码如下:

 

public interface ICollection : IEnumerable

{

    void CopyTo(Array array, int index);



    int Count { [__DynamicallyInvokable] get; }

}



public interface IList : ICollection, IEnumerable

{

    // Methods

    int Add(object value);

    void Clear();

    bool Contains(object value);

    int IndexOf(object value);

    void Insert(int index, object value);

    void Remove(object value);

    void RemoveAt(int index);



}



public interface IDictionary : ICollection, IEnumerable

{

    // Methods

    [__DynamicallyInvokable]

    void Add(object key, object value);

    [__DynamicallyInvokable]

    void Clear();

    [__DynamicallyInvokable]

    bool Contains(object key);

    [__DynamicallyInvokable]

    IDictionaryEnumerator GetEnumerator();

    [__DynamicallyInvokable]

    void Remove(object key);



    // Properties

    bool IsFixedSize { [__DynamicallyInvokable] get; }

    bool IsReadOnly { [__DynamicallyInvokable] get; }

    object this[object key] { [__DynamicallyInvokable] get; [__DynamicallyInvokable] set; }

    ICollection Keys { [__DynamicallyInvokable] get; }

    ICollection Values { [__DynamicallyInvokable] get; }

}

 

上面说的都是接口,它们内部都是些方法声明而已。具体的类,比如System.Array和System.Collections.ArrayList里面才有具体的方法实现。

System.Collections.ArrayList是System.Object对象的集合。

二、创建自己的集合:

一种方式是我们手动实现集合的所有方法,另一种方式是我们继承System.Collections.CollectionBase类。CollectionBase类已经实现了IEnumerable和ICollection、IList接口。

1、在继续之前,我们先来看看CollectionBase是怎么实现的:

[Serializable, ComVisible(true), __DynamicallyInvokable]

public abstract class CollectionBase : IList, ICollection, IEnumerable

{

    // Fields

    private ArrayList list;



    // Methods

    protected CollectionBase();

    protected CollectionBase(int capacity);

    public void Clear();

    public IEnumerator GetEnumerator();

    protected virtual void OnClear();

    protected virtual void OnClearComplete();

    protected virtual void OnInsert(int index, object value);

    protected virtual void OnInsertComplete(int index, object value);

    protected virtual void OnRemove(int index, object value);

    protected virtual void OnRemoveComplete(int index, object value);

    protected virtual void OnSet(int index, object oldValue, object newValue);

    protected virtual void OnSetComplete(int index, object oldValue, object newValue);

    protected virtual void OnValidate(object value);

    public void RemoveAt(int index);

    void ICollection.CopyTo(Array array, int index);

    int IList.Add(object value);

    bool IList.Contains(object value);

    int IList.IndexOf(object value);

    void IList.Insert(int index, object value);

    void IList.Remove(object value);



    // Properties

  

    public int Capacity { get; set; }

    public int Count { [__DynamicallyInvokable] get; }

    protected ArrayList InnerList { get; }

    protected IList List { get; }

    bool ICollection.IsSynchronized { get; }

    object ICollection.SyncRoot { get; }

    bool IList.IsFixedSize { get; }

    bool IList.IsReadOnly { get; }

    object IList.this[int index] { get; set; }

}



 


可知,CollectionBase是基于ArrayList的,GetEnumerator便是对IEnumerable接口的实现,它返回了一个IEnumerator,那么这个IEnumerator内部到底做了什么使得ICollection可以迭代(通过foreach)呢,显然,CollectionBase没有做这个工作,这个工作是由ArrayList来做的。具体来说是这样:CollectionBase有一个list数据成员,它不是属性(名称用camel命名法,且没有getter/setter),InnerList也是CollectionBase的属性,这个属性的代码以及GetEnumerator的代码如下:

protected ArrayList InnerList

{

    get

    {

        if (this.list == null)

        {

            this.list = new ArrayList();

        }

        return this.list;

    }

}



public IEnumerator GetEnumerator()

{

    return this.InnerList.GetEnumerator();

}
View Code

可以看到,确实是基于ArrayList的。
2、那么,我们来看看ArrayList是怎么实现迭代的:

public class ArrayList : IList, ICollection, IEnumerable, ICloneable

{

    // Fields

    private const int _defaultCapacity = 4;

    private object[] _items;

    private int _size;

    

    。。。



   public virtual IEnumerator GetEnumerator()

    {

        return new ArrayListEnumeratorSimple(this);

    }



    。。。



    

   private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable

    {

        // Fields

        private object currentElement;

        private static object dummyObject;

        private int index;

        [NonSerialized]

        private bool isArrayList;

        private ArrayList list;

        private int version;



        // Methods

        static ArrayListEnumeratorSimple();

        internal ArrayListEnumeratorSimple(ArrayList list);

        public object Clone();

        public bool MoveNext();

        public void Reset();



        // Properties

        public object Current { get; }

    }



}
View Code

可以看到ArrayList自己也没干活,而是把迭代任务交给了自己的一个内部类ArrayListEnumeratorSimple,其中有一个构造的修饰符是internal,意思是这个方法只能在程序集内部调用。该类有一个list成员,这个成员便是当ArrayList的GetEnumerator方法调用时把this,即ArrayList自己传给了ArrayListEnumeratorSimple的构造,从而ArrayListEnumeratorSimple的list也就得到初始化了。具体实现代码如下:

 private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable

{

    // Fields

    private object currentElement;

    private static object dummyObject;

    private int index;

    private bool isArrayList;

    private ArrayList list;

    private int version;



    // Methods

    static ArrayListEnumeratorSimple()

    {

        dummyObject = new object();

    }



    internal ArrayListEnumeratorSimple(ArrayList list)

    {

        this.list = list;

        this.index = -1;

        this.version = list._version;

        this.isArrayList = list.GetType() == typeof(ArrayList);

        this.currentElement = dummyObject;

    }



    public object Clone(){/*...*/}

    public bool MoveNext()

    {

        if (this.version != this.list._version)

        {

            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

        }

        if (this.isArrayList)

        {

            if (this.index < (this.list._size - 1))

            {

                this.currentElement = this.list._items[++this.index];

                return true;

            }

            this.currentElement = dummyObject;

            this.index = this.list._size;

            return false;

        }

        if (this.index < (this.list.Count - 1))

        {

            this.currentElement = this.list[++this.index];

            return true;

        }

        this.index = this.list.Count;

        this.currentElement = dummyObject;

        return false;

    }



    public void Reset()

    {

        if (this.version != this.list._version)

        {

            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));

        }

        this.currentElement = dummyObject;

        this.index = -1;

    }



    public object Current

    {

        get

        {

            object currentElement = this.currentElement;

            if (dummyObject != currentElement)

            {

                return currentElement;

            }

            if (this.index == -1)

            {

                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted"));

            }

            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded"));

        }

    }



}
View Code

迭代的原理很简单:

MoveNext的工作就是将Index加1,然后将新的Index指向的元素设置为当前元素currentElement,这些执行成功就返回true,否则返回false。
Reset的工作就是将Index置为-1。

Current属性则返回当前元素。

 3、一个简单的自定义集合:这个就暂时先略了吧,有了上面的分析,我们通过继承CollectionBase的方法去自定义一个集合就很简单了。

三、foreach循环迭代的原理:参考http://li19910722.blog.163.com/blog/static/136856822201359105740400/

只有当一个集合实现了IEnumerable接口,才能在这个集合对象上进行foreach。由参考链接中的内容可知,其实实不实现IEnumerable接口无所谓,只要你有一个GetEnumerator方法返回一个Enumerator即可。要知道的是,Enumerator的MoveNext在将索引加1的同时,返回了true,如果到达界限则返回false,之所以返回bool值,是为了给foreach的in操作作判断用的,为真则执行循环体,否则退出循环。

但这还不够,我还是觉得有些东西没弄明白,比如迭代的时候可以用yield什么的,这其中的原理又是什么呢?看了这篇文章http://jangmon.blogbus.com/logs/36380490.html,总结一下:

foreach (Person p in persons)

{

    Console.WriteLine(p);

}

//上面的代码会被C#解析成:

IEnumerator enumerator = persons.GetEnumerator();

while (enumerator.MoveNext())

{

    Person p = (Person) enumerator.Current;

    Console.WriteLine(p);

}

//可以看出,这和我们上面的说法互补。
View Code
//定义一个HelloCollection

public class HelloCollection : IEnumerable

{

    public IEnumerator GetEnumerator()

    {

        yield return "Hello";

        yield return "World";

    }

}



//开始迭代

static void Main(string[] args)

{



    HelloCollection helloCollection=new HelloCollection ();

    foreach (string s in helloCollection )

    {

        Console.WriteLine(s);

    }

}

//HelloCollection最终转换成的代码

public class HelloCollection : IEnumerable

{

    public IEnumerator GetEnumerator()

    {

        Enumertor enumerator = new Enumerator(0);

        return enumerator;

    }



    public class Enumertor : IEnumerator, IDisposable

    {

        private int state;

        private object current;



        public Enumertor(int state)

        {

            this.state = state;

        }

    }

        bool System.Collections .IEnumerator .MoveNext()

        {

            switch (state)

            {

                case 0:

                    current = "Hello";

                    state = 1;

                    return true;

                case 1:

                    current = "World";

                    state = 2;

                    return true ;

                case 2:

                    break ;

            }

            return false ;

        }



        void System.Collections .IEnumerator .Reset()

        {

            throw new NotSupportedException();

        }



        object System.Collections .IEnumerator .Current

        {

            get { return current; }

        }

        void IDisposable.Dispose()

        {

        }

}
View Code

我觉得上篇文章的GetEnumerator方法在返回一个new出的Enumerator的时候,应该将state初始化为0,应该是作者是或者作者所参考的书籍的代码中的一个小错误。
GetEnumerator方法是foreach迭代默认在被迭代集合对象上调用的方法,这是可以定制的:

public class MusicTitles:IEnumerable 

{

    string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" };



    public IEnumerator GetEnumerator()  /*顺序迭代*/

    {

        for (int i = 0; i < 4; i++)

            yield return names[i];

    }



    public IEnumerator Reverse()  /*逆序迭代*/

    {

        for (int i = 3; i >= 0; i--) 

            yield return names[i];

    }

}



static void Main(string[] args)

{



    MusicTitles titles = new MusicTitles();

    //这个迭代没有指明,则默认就使用被迭代集合的GetEnumerator方法

    foreach (string title in titles)

    {

        Console.WriteLine(title);

    }



    Console.WriteLine();

    //这个指明了迭代所用的方法

    foreach (string title in titles.Reverse())

    {

        Console.WriteLine(title);

    }

}
View Code

 四、一点体会:关于迭代,说了上面一堆,又想起Linq查询,大意是只有访问一个Element时才会作真正的查询,不知道Linq跟foreach有什么关系,日后学到了再说,反正有了一点体会,也不知道对不对:foreach跟for的区别在于,for在遍历一个集合的时候,集合中的元素必须是已经存在于内存的,而foreach由于迭代每一个项都走了MoveNext方法,从而我们可以在真正迭代到某元素的时候再临时地在MoveNext里把这个元素查出来(这取决于MoveNext的具体实现了),这似乎有一点延迟加载的味道,不知道和Linq延时查询以及EF的延时加载有什么内存关联,日后再说吧。

 

你可能感兴趣的:(C#)