GetEnumerator方法的实现

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
下面是GetEnumerator方法的实现:

C#:
 IEnumerator IEnumerable.GetEnumerator()  
 {
     return (IEnumerator) GetEnumerator();
 }

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

这是我看到正在实现GetEnumerator方法的另一种方式:

C#:
上市 IEnumerator GetEnumerator()
{
    return new ListEnumerator();
}

private class ListEnumerator : IEnumerator
{
    
}


我的问题是,第一个实现可以写成第二个实现吗?
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
The first example is an explicit implementation of the interface while the second is not. An explicit implementation is one that can only be accessed by casting an object of the type as the interface type. For example, if I just add a class to a project, specify that it implements the IEnumerable interface and then tell the IDE to add missing members, this is what I get:
C#:
class Class1 : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
基于该实现,我可以这样做:
C#:
var c1 = new Class1();
var e = c1.GetEnumerator();
If I click on the GetEnumerator method and select the To explicit implementation option, the code is changed to this:
C#:
class Class1 : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
The previous usage of the c1 class and its GetEnumerator method will no longer compile. The compiler will tell you:
'Class1'不包含'GetEnumerator'的定义,并且找不到可以接受的扩展方法'GetEnumerator'接受福彩12选5走势图为'Class1'的第一个参数(您是否缺少using指令或程序集引用?)
为了访问接口成员的显式实现,您必须将对象强制转换为接口福彩12选5走势图,例如
C#:
var c1 = new Class1();
var ce1 = (IEnumerable) c1;
var e = ce1.GetEnumerator();
In the case of the IEnumerable and IEnumerable<T> interfaces, whether the implementation is explicit or not rarely makes a difference. That's because such objects are generally enumerated using a 前言 loop and, internally, it will cast the object being enumerated as one of those interfaces.
C#:
var c1 = new Class1();

foreach (var item in c1)
{
    // ...
}
The other difference between those examples appears to be that the first one uses a type implementing IEnumerator that is declared outside the type it enumerates while the second example uses a type declared inside the type it enumerates. Either is valid but a nested type can only be used to enumerate instances of the type it is declared in, while an independent type could, in theory, be used to enumerate multiple other types.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
谢谢 金西尼,很好解释。

金西尼,如果您正在创建自己的自定义福彩12选5走势图,并且想要确保可以在自己的自定义福彩12选5走势图上使用foreach循环,那么将使用哪种方式在自定义类中实现IEnumerable接口?
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
The only reason I have found to create an explicit implementation is if there's a clash between members of multiple interfaces. There may be other reasons that I'm not aware of but I would generally not make the implementation explicit. That said, given that IEnumerable and its generic counterpart are primarily to facilitate 前言 loops, making implementations of those can make the class appear cleaner without sacrificing functionality.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
C#:
using System;
using System.Collections;


class Person
{
    private string fistname;
    private string lastname;

    public string Fname
    {
        set
        {
            this.fistname = value;
        }

        get
        {
            return this.lastname;
        }
    }
}


class People : IEnumerable
{
    private Person[] _people;  //create an array called people that hold object Person

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

    public IEnumerator GetEnumerator()
    {
        return new PeopleEnumerator(_people);
    }


    private class PeopleEnumerator : IEnumerator
    {
        private int _currentIndex = -1;

        public PeopleEnumerator(Person[] list)
        {
            _people = list;   //here it does not recognise _people, but why?
        }

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

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

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

        }
}


我收到一个错误:

C#:
Severity    Code    Description    Project    File    Line    Suppression State
Error    CS0120    An object reference is required for the non-static field, method, or property 'People._people'    ConsoleApp45    c:\users\mp88_\source\repos\ConsoleApp45\ConsoleApp45\Program.cs    49    Active

我知道为什么会收到此错误:私有嵌套类
C#:
PeopleEnumerator
正在尝试访问
C#:
_people
在外部类中声明的
C#:
People
as a private member.
但是我不明白的是,我认为嵌套成员内部类可以访问私有成员,不是吗?
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
The issue is not that you cannot access the _people field because it is private. The issue is that you are not accessing it via an instance of the People class, as the error message says. The fact that the PeopleEnumerator class is declared inside the People class does not mean that each PeopleEnumerator instance has implicit access to a People instance. Your People class is creating an instance of the PeopleEnumerator class so that PeopleEnumerator object would have to know about the People object that created it specifically.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
啊,我明白了。
因此,为了澄清一下,嵌套类只能通过外部类的实例访问外部类的私有成员吗?
谢谢你
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
Code in a nested class is just like code in any other class. It can access anything that is in scope and it requires a reference to an instance of a type to invoke instance members of that type. Private fields of an outer type are in scope for any code within that type, so code in a nested class can access them. That doesn't change the fact that an instance is required to access an instance member though. Only 分享d members can be access without an instance.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
当您创建实现IEnumerable接口的类时,是否还必须实现IEnumerator是强制性的?
我问的原因是,我看到了一些实现IEnumerator接口的代码,例如以下代码:

C#:
using System;
using System.Collections;


class Person
{
    private string fistname;
    private string lastname;

    public string Fname
    {
        set
        {
            this.fistname = value;
        }

        get
        {
            return this.fistname;
        }
    }
}


class People : IEnumerable
{
    private Person[] _people;  //create an array called people that hold object Person

    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

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

    public IEnumerator GetEnumerator()
    {
        return new PeopleEnumerator(_people);
    }


    private class PeopleEnumerator : IEnumerator
    {
        private Person[] _plist;

        private int _currentIndex = -1;

        public PeopleEnumerator(Person[] list)
        {
            _plist = list;
        }

        public bool MoveNext()
        {
            _currentIndex++;
            return (_currentIndex < _plist.Length);
        }

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

        public object Current
        {
            get
            {
                try
                {
                    return _plist[_currentIndex];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }

        }
    }
}

但是另一方面,我看到了完全忽略实现IEnumerator接口的代码,如下所示:

C#:
class MyArrayList : IEnumerable
{
    object[] m_Items = null;
    int freeIndex = 0;

    public MyArrayList()
    {
        // For the sake of simplicity lets keep them as arrays
        // ideally it should be link list
        m_Items = new object[100];
    }

    public void Add(object item)
    {
        // Let us only worry about adding the item
        m_Items[freeIndex] = item;
        freeIndex++;
    }

    // IEnumerable Member
    public IEnumerator GetEnumerator()
    {
        foreach (object o in m_Items)
        {
            // Lets check for end of list (its bad code since we used arrays)
            if(o == null)
            {
                break;
            }

            // Return the current element and then on next function call
            // resume from next element rather than starting all over again;
            yield return o;
        }
    }
}


这两段代码都试图实现一个目标-即能够使用"foreach"有关其自定义福彩12选5走势图的说明。
因此,我不明白为什么在没有实现的情况下仍然可以实现相同目标的情况下,为什么要去实现IEnumerator接口。
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
The only requirement when you implement the IEnumerable interface is that you implement a method named GetEnumerator and it returns an IEnumerator instance. That's it. Obviously if you are to return an IEnumerator though, that needs to be implemented somewhere. Before the existence of iterators, i.e. methods that use yield to return multiple objects one at a time, you had no choice but to return an object that implemented IEnumerator directly, which often meant doing it yourself. The addition of iterators has made things easier but obviously old code will still do it the old way and some people who aren't comfortable with iterators may still do it the old way even now.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
嗯,我还是有点困惑。
您绝对需要实现IEnumerable接口-已理解。
下面的代码实现了IEnumerable接口,但是没有返回IEnumerator,对吗?

C#:
上市 IEnumerator GetEnumerator()
    {
        foreach (object o in m_Items)
        {
            // Lets check for end of list (its bad code since we used arrays)
            if(o == null)
            {
                break;
            }

            // Return the current element and then on next function call
            // resume from next element rather than starting all over again;
            yield return o;
        }
    }
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
It is returning an IEnumerator. It must be, because that's the return type of the method. What it is not doing is explicitly returning an object of a type that you declared that implements IEnumerator. When you use yield return, you are making use of language features that will generate code to return an IEnumerator object create by the system. Try calling that method and assigning the result to a variable and then examine it in the debugger to see exactly what type it is.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
510


我尝试调用该方法 IEnumerator.GetEnumerator() (即IEnumerable接口中的唯一方法),将其分配给变量 t 从图像中可以看到, t 是这种福彩12选5走势图"System.Collections.IEnumerator" (aka "IEnumerator").

金西尼 那是你想达到的目的吗?

谢谢你
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
Yes, that is exactly what I was saying. The method's return type is IEnumerator so the object it returns must be that type. The actual type name indicates that it is an anonymous type, which is something generated on the fly, as required by the situation. When you use yield return and return an object of type T, the system generates a type that is capable of enumerating a list of objects of type T.
 

mp3909

知名会员
已加入
2018年4月22日
留言内容
61
地点
英国
编程经验
3-5
请参阅此代码中的注释。

C#:
using System;
using System.Collections;

class Person
{
    public string firstname { set; get; }
    public string lastname { set; get; }
}

class People : IEnumerable
{
    private Person[] _pple;

    public People(Person[] p)
    {
        _pple = new Person[p.Length];
        addPerson(p);
    }

    public void addPerson(Person[] p)
    {
        for (int i = 0; i < p.Length; i++)
        {
            _pple[i] = p[i];
        }
    }

    public IEnumerator GetEnumerator()
    {
        foreach(Person pp in _pple)
        {
            yield return pp;
        }
    }

}


class Program
{
    public static void Main()
    {
        Person[] p = new Person[4]
            {
            new Person() {firstname = "John", lastname = "A" },
            new Person() {firstname  = "Jim", lastname = "B"},
            new Person() {firstname = "Jack", lastname = "C"},
            new Person() {firstname = "James", lastname = "D"}
            };

        People pps = new People(p);

        foreach (Person a in pps)
        {
            Console.WriteLine(a.firstname);
        }

        var x = pps.GetEnumerator();
        while(x.MoveNext())
        {
            Console.WriteLine(x.Current);   //I am trying to replicate the above foreach loop but I cannot seem to print out the firstname here
        }
    }
}
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
In this day and age, you should be implementing generic versions of interfaces if they exist. If you implement a generic interface for which a non-generic version exists as well, you should implement both and then defer to the generic implementation in the non-generic implementation. In the case of your People class, you should be doing this:
C#:
class People : IEnumerable, IEnumerable<Person>
{
    private Person[] _pple;

    public People(Person[] p)
    {
        _pple = new Person[p.Length];
        addPerson(p);
    }

    public void addPerson(Person[] p)
    {
        for (int i = 0; i < p.Length; i++)
        {
            _pple[i] = p[i];
        }
    }

    public IEnumerator<Person> GetEnumerator()
    {
        foreach (var pp in _pple)
        {
            yield return pp;
        }
    }

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

}
The implementation of IEnumerable.GetEnumerator is made explicit, so it is only accessible if a People instance is explicitly cast as type IEnumerable. That means that your current code would be calling the implementation of IEnumerator<Person>.GetEnumerator, in which case the Current property will be type Person rather than type object, so there's no cast necessary to access members of the Person type:
C#:
var x = pps.GetEnumerator();
while (x.MoveNext())
{
    Console.WriteLine(x.Current.firstname);
}
By the way, if you call GetEnumerator directly, rather than using a 前言 loop, then you should dispose the object it returns when you're done with it. That means that you should create it with a using statement:
C#:
using (var x = pps.GetEnumerator())
{
    while (x.MoveNext())
    {
        Console.WriteLine(x.Current.firstname);
    }
}
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,524
地点
悉尼,澳大利亚
编程经验
10+
是否有原因"public"以下不允许使用关键字:
因为这是一个明确的实现。明确实现的接口成员永远不能通过类福彩12选5走势图的引用进行访问,而始终可以通过接口福彩12选5走势图的引用进行访问,因此访问修饰符无用或无意义,因此是不允许的。
 
最佳 底部