C#高级程序设计(七)——迭代

本文探讨.NET中的迭代器模式,重点是C#如何通过IEnumerator和IEnumerable接口实现迭代,特别是C#2.0引入的yield关键字简化迭代器的使用。文章还解释了迭代器在LINQ查询中的作用,展示了使用迭代器实现LINQ的Where方法。

.NET使用IEnumerator接口和IEnumerable接口(以及它们的泛型版本)实现迭代器模式,迭代是LINQ的核心。

接口定义如下:

    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

    public interface IEnumerator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }

    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }

    public interface IEnumerator<out T> : IDisposable, IEnumerator
    {
        T Current { get; }
    }

一、C#1实现迭代方式

C#1通过实现接口实现迭代,下面是一个例子,可以指定起始的顺序来迭代集合,其中的一些注意点在注释中有所体现。

public class IterationSample : IEnumerable<string>
    {
        string[] values;
        int startingPoint;

        public IterationSample(string[] values, int startingPoint)
        {
            this.values = values;
            this.startingPoint = startingPoint;
        }
        public IEnumerator<string> GetEnumerator()
        {
            // This is important to return a new instance of iterator
            // so that each foreach loop will have its own iterator.
            return new IterationSampleIterator(this); 
        }

        // Class that implement IEnumerable<T> must also implement IEnumerable, 
        // here is a workaround to implement GetEnumerator method that defined 
        // in both IEnumerable<T> and IEnumerable. 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator(); 
        }

        // Inner class can access to private members of its parent class. 
        class IterationSampleIterator : IEnumerator<string>
        {
            IterationSample parent;
            int position;

            internal IterationSampleIterator(IterationSample parent)
            {
                this.parent = parent;
                position = -1;
            }

            public bool MoveNext()
            {
                if (position != parent.values.Length)
                {
                    position++;
                }

                return position < parent.values.Length;
            }

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

            public string Current
            {
                get
                {
                    if (position == -1 ||
                    position == parent.values.Length)
                    {
                        throw new InvalidOperationException();
                    }
                    int index = position + parent.startingPoint;
                    index = index % parent.values.Length;
                    return parent.values[index];
                }
            }

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

            public void Dispose()
            {
            }
        }
    }

    string[] values = { "a", "b", "c", "d", "e" };
    IterationSample collection = new IterationSample(values, 3);
    foreach (object x in collection)
    {
        Console.WriteLine(x);
    }

二、C#2实现迭代方式

C#2提供了yield关键字,使得迭代实现代码大大简化,下面使用yield方式来改写上面的实现。

    public class IterationSample : IEnumerable<string>
    {
        string[] values;
        int startingPoint;

        public IterationSample(string[] values, int startingPoint)
        {
            this.values = values;
            this.startingPoint = startingPoint;
        }
        public IEnumerator<string> GetEnumerator()
        {
            for (int index = 0; index < values.Length; index++)
            {
                yield return values[(index + startingPoint) % values.Length];
            }
        }

        // Class that implement IEnumerable<T> must also implement IEnumerable, 
        // here is a workaround to implement GetEnumerator method that defined 
        // in both IEnumerable<T> and IEnumerable. 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator(); 
        }
    }

    string[] values = { "a", "b", "c", "d", "e" };
    IterationSample collection = new IterationSample(values, 3);
    foreach (object x in collection)
    {
        Console.WriteLine(x);
    }

yield 关键字向编译器指示它所在的方法是迭代器块。   编译器生成一个类来实现迭代器块中表示的行为。   在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。   这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。   yield 关键字也可与 break 结合使用,表示迭代结束。

yield return <expression>;
yield break;

三、迭代器

迭代器是 C# 2.0 中的新功能。迭代器是方法、get 访问器或运算符,它使您能够在结构中支持 foreach 迭代,而不必实现整个 IEnumerable 接口。您只需提供一个迭代器,即可遍历类中的数据结构。当编译器检测到迭代器时,它将自动生成 IEnumerable 或 IEnumerable<T> 接口的相关迭代方法,进一步简化了迭代的开发。

public class Program
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/

四、迭代器实现LINQ查询

下面的例子使用迭代器实现了LINQ查询中Where方法。

public static IEnumerable<T> Where<T>(IEnumerable<T> source,
Predicate<T> predicate)
{
if (source == null || predicate == null)
{
throw new ArgumentNullException();
}
return WhereImpl(source, predicate);
}
private static IEnumerable<T> WhereImpl<T>(IEnumerable<T> source,
Predicate<T> predicate)
{
foreach (T item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
...
IEnumerable<string> lines = LineReader.ReadLines("../../FakeLinq.cs");
Predicate<string> predicate = delegate(string line)
{ return line.StartsWith("using"); };
foreach (string line in Where(lines, predicate))
{
Console.WriteLine(line);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值