关于IEnumerable与IEnumerator还是不懂

来源:互联网 发布:js父元素 编辑:程序博客网 时间:2024/05/22 05:25
发表于: 2013-11-08 01:41:55
http://bbs.csdn.net/topics/390636800本帖最后由 jxfinest 于 2013-11-08 02:37:59 编辑
using System;
using System.Collections;

namespace ConsoleApplication11
{
    class Program
    {
        public class D9TecMembers:IEnumerable //把:IEnumerable去掉貌似也可以(去不去掉无所谓,因为代码已经实现了相应的接口)。再把代码一中的GetEnumerator换任意名,就不行,而再把IEnumerator改成IEnumerable就行了,感觉IEnumerable条件更宽松,可以这样说吧?后边跟什么名称的方法都可以,但IEnumerator就不行了,跟其它的方法名就不行(会出现“System.Collections.IEnumerator”不包含“GetEnumerator”的公共定义,因此 foreach 语句不能作用于“System.Collections.IEnumerator”类型的变量),必须与GetEnumerator()相互结合(如果有public IEnumerator GetEnumerator()这行语句的话,但是去掉也不会出错),所以由此得出结论,这里的GetEnumerator()并不是普通的方法名,它与IEnumerator相配的话下边对应的代码二为foreach (string title in titles),它与IEnumerable相配的时候是任意名的性质,下边的对应的代码二为foreach (string title in titles. GetEnumerator())。有哪位高手能为我解释一下这种现象的理由吗?
        {
            string[] names = { "Wei Gao", "Zhao Zhang", "Changji Song", "Zhike Chang", "Denian Zhang", "Zhaotian Yang", "Guangyue Cai" };
            public IEnumerator GetEnumerator()//代码一
            {
                for (int i = 0; i < names.Length; i++)
                {
                    yield return names[i];//返回迭代器
                }
            }
            public IEnumerable Reverse()
            {
                for (int i = names.Length - 1; i >= 0; i--)
                {
                    yield return names[i];
                }
            }
            public IEnumerable Subset(int index, int length)
            {

                for (int i = index; i < index + length; i++)
                {
                    yield return names[i];
                }
            }
        }

        static void Main(string[] args)
        {
            D9TecMembers titles = new D9TecMembers();

            foreach (string title in titles)//代码二
            {
                Console.WriteLine(title);
            }
            Console.WriteLine();
            foreach (string title in titles.Reverse())
            {
                Console.WriteLine(title);
            }
            Console.WriteLine();
            foreach (string title in titles.Subset(1, 2))
            {
                Console.WriteLine(title);
            }
Console.ReadKey();
        }
    }
}
输出
Wei Gao
Zhao Zhang
Changji Song
Zhike Chang
Denian Zhang
Zhaotian Yang
Guangyue Cai

Guangyue Cai
Zhaotian Yang
Denian Zhang
Zhike Chang
Changji Song
Zhao Zhang
Wei Gao

Zhao Zhang
Changji Song
请按任意键继续. . .
迭代器IEnumerableIEnumerator
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
回复次数:15
#1 得分:0回复于: 2013-11-08 02:33:09
代码一改成这样也行,这到底是怎么回事?
public IEnumerator   GetEnumerator()
            {
            return    this.names.GetEnumerator();
                //for (int i = 0; i < names.Length; i++)
                //{
                //    yield return names[i];//返回迭代器
                //}
            }
用Visual Studio开发 iOS、Android 应用,你也可以!
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#2 得分:0回复于: 2013-11-08 02:51:52
是这样的,如果要对一个对象使用foreach,那么它必须包含一个GetEnumerator()方法,无参数,返回类型任意,同时返回的类型必须包含Current属性和MoveNext方法。编译器会自动去绑定,因此不需要IEnumerable和IEnumerator。其实接口的本质就是让编译器去绑定一个对象的方法,在这里,C#编译器的确不依赖接口。

下面的代码不使用IEnumerable和IEnumerator:

C# code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication1
{
    class Program
    {
        public class D9TecMembers
        {
            public class DefaultEnumerator
            {
                private string[] value;
                private int n = 0;
                public DefaultEnumerator(string[] value) { this.value = value; }
                public object Current
                {
                    get return value[n]; }
                }
 
                public bool MoveNext()
                {
                    return ++n < value.Length;
                }
            }
 
            string[] names = { "Wei Gao""Zhao Zhang""Changji Song""Zhike Chang""Denian Zhang""Zhaotian Yang""Guangyue Cai" };
             
            public DefaultEnumerator GetEnumerator()
            {
                return new DefaultEnumerator(names);
            }
        }
 
        static void Main(string[] args)
        {
            D9TecMembers titles = new D9TecMembers();
 
            foreach (string title in titles)
            {
                Console.WriteLine(title);
            }
        }
    }
}


至于yield,则是C#编译器自动为你产生两个类,一个包含GetEnumerator,一个包含Current和MoveNext
关注CSDN论坛微博 送CSDN积分大礼包
对我有用[1] 丢个板砖[0] 引用 | 举报 | 管理
#3 得分:0回复于: 2013-11-08 03:06:02
本帖最后由 caozhy 于 2013-11-08 03:14:54 编辑
C# code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication1
{
    class A
    {
        public B GetEnumerator() { return new B(); }
    }
 
    class B
    {
        public string Current { get return "Hello World"; } }
        public bool MoveNext() { return new Random(Guid.NewGuid().GetHashCode()).Next(0, 10) != 0; }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            foreach (string in new A())
            {
                Console.WriteLine(s);
            }
        }
    }
}


这段代码展示了让foreach工作的最简代码

上面我们分出A B是因为告诉你,A B可以是不同的类。

当然我们也可以在一个类中实现:
C# code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication1
{
    class A
    {
        public A GetEnumerator() { return this; }
        public string Current { get return "Hello World"; } }
        public bool MoveNext() { return new Random(Guid.NewGuid().GetHashCode()).Next(0, 10) != 0; }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            foreach (string in new A())
            {
                Console.WriteLine(s);
            }
        }
    }
}


下面是使用yield的等价版本:

C# code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication1
{
    class Program
    {
        static IEnumerable<string> foo()
        
            while (new Random(Guid.NewGuid().GetHashCode()).Next(0, 10) != 0)
                yield return "Hello World";
        }
        static void Main(string[] args)
        {
            Console.WriteLine("A相当于:" + foo().GetType());
            Console.WriteLine("B相当于:" + foo().GetEnumerator().GetType());
            Console.WriteLine(foo().GetType() + "的方法:");
            foreach (var item in foo().GetType().GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Select(x => x.Name)) 
                Console.WriteLine(item);
            Console.WriteLine();
            foreach (string in foo())
            {
                Console.WriteLine(s);
            }
        }
    }
}


A相当于:ConsoleApplication1.Program+<foo>d__0 //这是C#编译器为你生成的类,兼具了上面代码 A B 的作用。
B相当于:ConsoleApplication1.Program+<foo>d__0
ConsoleApplication1.Program+<foo>d__0的方法:
System.Collections.Generic.IEnumerable<System.String>.GetEnumerator
System.Collections.IEnumerable.GetEnumerator
MoveNext
System.Collections.Generic.IEnumerator<System.String>.get_Current
System.Collections.IEnumerator.Reset
System.IDisposable.Dispose
System.Collections.IEnumerator.get_Current
ToString
Equals
GetHashCode
GetType
Finalize
MemberwiseClone

Hello World
...
Hello World
Press any key to continue . . .
当你用foreach (object s in foo())的时候,调用非泛型的Current版本,否则调用泛型的版本。