Billpeng Space

技术源自生活
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

IList,ICollection,IEnumerable,IEnumerator,IQueryable

Posted on 2013-05-10 09:39  billpeng  阅读(304)  评论(0编辑  收藏  举报

IList 是 ICollection 接口的子代,并且是所有非泛型列表的基接口。IList 实现有三种类别:只读、固定大小和可变大小。无法修改只读 IList。固定大小的 IList 不允许添加或移除元素,但允许修改现有元素。可变大小的 IList 允许添加、移除和修改元素。

ICollection 接口是 System.Collections 命名空间中类的基接口。ICollection 接口扩展 IEnumerable;IDictionary 和 IList 则是扩展 ICollection 的更为专用的接口。 IDictionary 实现是键/值对的集合,如 Hashtable 类。 IList 实现是值的集合,其成员可通过索引访问,如 ArrayList 类。  某些集合(如 Queue 类和 Stack 类)限制对其元素的访问,它们直接实现 ICollection 接口。  如果 IDictionary 接口和 IList 接口都不能满足所需集合的要求,则从 ICollection 接口派生新集合类以提高灵活性。定义所有非泛型集合的大小、枚举器和同步方法。

IQueryable 提供对未指定数据类型的特定数据源的查询进行计算的功能,IQueryable 接口由查询提供程序实现。 该接口只能由同时实现 IQueryable(Of T) 的提供程序实现。 如果该提供程序不实现 IQueryable(Of T),则无法对提供程序数据源使用标准查询运算符。 IQueryable 接口继承 IEnumerable 接口,以便在前者表示一个查询时可以枚举该查询的结果。 枚举强制执行与 IQueryable 对象关联的表达式树。 “执行表达式树”的定义是查询提供程序所特有的。 例如,它可能涉及将表达式树转换为适用于基础数据源的查询语言。 在调用 Execute 方法时将执行不返回可枚举结果的查询。

IEnumerable 公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。

IEnumerator 是所有非泛型枚举数的基接口。有关此接口的泛形版本,请参见 IEnumerator(Of T)。C# 语言的 foreach 语句(在 Visual Basic 中为 for each)隐藏了枚举数的复杂性。 因此,建议使用 foreach,而不直接操作枚举数。枚举数可用于读取集合中的数据,但不能用于修改基础集合。最初,枚举数定位在集合中第一个元素前。 Reset 方法还会将枚举数返回到此位置。 在此位置,调用 Current 属性会引发异常。 因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数提前到集合的第一个元素。在调用 MoveNext 或 Reset 之前,Current 返回同一对象。 MoveNext 将 Current 设置为下一个元素。如果 MoveNext 越过集合的末尾,枚举数就会被放置在此集合中最后一个元素的后面,且 MoveNext 返回 false 当枚举数位于此位置时,对 MoveNext的后续调用也返回 false 如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。 若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext。只要集合保持不变,枚举数就保持有效。 如果对集合进行了更改(如添加、修改或删除元素),则枚举数将失效且不可恢复,并且下一次对 MoveNext或 Reset 的调用将引发 InvalidOperationException。 如果在 MoveNext 和 Current 之间修改集合,那么即使枚举数已经无效,Current 也将返回它所设置成的元素。枚举数没有对集合的独占访问权;因此,从头到尾对一个集合进行枚举在本质上不是一个线程安全的过程。 即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。 若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

下面的代码示例演示如何实现自定义集合的 IEnumerable 和 IEnumerator 接口。 在此示例中,没有显式调用这些接口的成员,但实现了它们,以便支持使用 foreach(在 Visual Basic 中为 for each)循环访问该集合。

using System;
using System.Collections;

public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }

    public string firstName;
    public string lastName;
}

public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] = pArray[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
       return (IEnumerator) GetEnumerator();
    }

    public PeopleEnum GetEnumerator()
    {
        return new PeopleEnum(_people);
    }
}

public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    // Enumerators are positioned before the first element
    // until the first MoveNext() call.
    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Person Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

class App
{
    static void Main()
    {
        Person[] peopleArray = new Person[3]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
        };

        People peopleList = new People(peopleArray);
        foreach (Person p in peopleList)
            Console.WriteLine(p.firstName + " " + p.lastName);

    }
}