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
{
    
}


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

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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”的定义,无法找到可访问的扩展方法'get1umerator'接受“类别1”类型的第一个参数(您是否缺少使用指令或装配参考?)
为了访问接口成员的显式实现,必须将对象作为接口类型转换为界面类型,例如,
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 Foreach. 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.
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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 Foreach. 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.
但我不明白的是,我认为私人成员可以通过尼斯特的内部课程,没有?
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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.
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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
创建实现IEnumable接口的类时,它必须强制使用Inumerator?
我问的原因是我已经看到了一些代码,它可以实现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();
                }
            }

        }
    }
}

但另一方面,我已经看到了完全忽略这样的代码,如此:

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"他们的自定义类型的声明。
所以我不明白为什么可以在没有实现的情况下实现相同的目标时,为什么可以去实现Ienumerator接口。
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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接口 - 理解。
下面的代码正在实现IEnumable接口,但它不返回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;
        }
    }
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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() (即IENUMENTER接口中的一个和唯一方法),将其分配给变量 t 正如您从图像中看到, t 是类型的"system.collections.ienumerator." (aka "IEnumerator").

jmplhinney. 这是你想要得到的吗?

谢谢你
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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日
消息
1,157
地点
挪威
编程经验
10+
当前是类型对象,因此您将其投用为人: ((Person)x.Current).firstname
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
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 Foreach. 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);
    }
}
 

jmplhinney.

C#论坛主持人
工作人员
加入
2011年4月23日
消息
3,706
地点
悉尼,澳大利亚
编程经验
10+
有理由有理由"public"以下不允许关键字:
因为它是一个明确的实现。明确实现的接口成员永远不会通过类类型的引用访问,并且始终通过接口类型的引用可访问,因此访问修改器无用或有意义,因此不允许。
 
最佳 底部