Реализовать собственную коллекцию в C #, но нужно изменить IEnumerable? - PullRequest
2 голосов
/ 14 марта 2019

Я нашел отличную статью, иллюстрирующую IEnumerable, а IEnumerator

https://programmingwithmosh.com/csharp/ienumerable-and-ienumerator/

ниже приведен исходный код:

public class List : IEnumerable
{
    private object[] _objects;

    public List()
    {
        _objects = new object[100];
    }

    public void Add(object obj)
    {
        _objects[_objects.Count] = obj;
    }

    public IEnumerator GetEnumerator()
    {
        return new ListEnumerator();     //region 1
    } 

    private class ListEnumerator : IEnumerator 
    {
       private int _currentIndex = -1;
       public bool MoveNext()
       {
          _currentIndex++;
           return (_currentIndex < _objects.Count); 
       }
       public object Current
       {
         ...
       }
       public void Reset()
       {
           _currentIndex = -1;
       }
    }
}

Я нашел несколько ошибок в этой статье,вероятно, некоторые опечатки, такие как _objects.Count, должны быть _objects.Length, но есть одна фундаментальная проблема: как получить доступ к _objects в ListEnumerator?Поэтому я предполагаю, что мне нужно передать 'this' в область 1

 public IEnumerator GetEnumerator()
 {
    return new ListEnumerator(this);
 } 

и изменить ListEnumerator следующим образом:

private class ListEnumerator : IEnumerator 
{
   private int _currentIndex = -1;
   IEnumerable aggregate = null;
   public ListEnumerator(IEnumerable param)
   {
      aggregate  = param
   }

   public bool MoveNext()
   {
      _currentIndex++; 
      return (_currentIndex < aggregate.Count);   //important region
   }
   public object Current
   {
      //don't worry about the boundary check/exceptions
     get
     {
         return aggregate[_currentIndex];  //important region
     }
   }
...
}

Для того, чтобы сделать это, IEnumerable также должен быть похож на

interface IEnumerable 
{
   IEnumerator GetEnumerator();
   int Count{get;}
   object this[int itemIndex]{set;get;}
}

, но мы все знаем, что IEnumerable имеет только один метод GetEnumerator(), который необходимо реализовать.Итак, как мы можем это сделать, кто-нибудь может изменить код, чтобы он работал?

1 Ответ

2 голосов
/ 14 марта 2019

Существует проблема с кодом статьи, на которую вы ссылаетесь.

_objects является закрытым членом класса List, поэтому к нему нельзя получить доступ вне класса, даже изinnterclass List.

Даже если вы сделаете _objects общедоступным, к нему нельзя будет обратиться без создания объекта List класса.

Также нет способа IEnumerableИнтерфейс может быть изменен.

Я бы предложил следующие два подхода.

Подход 1

Измените ListEnumerator конструктор класса для принятия объекта изList class

private class ListEnumerator : IEnumerator 
{
    private List _list;
    public ListEnumerator(List list)
    {
        this._list = list;
    }
    private int _currentIndex = -1; 

    public bool MoveNext()
    {
        _currentIndex++;

        return (_currentIndex < this._list._objects.Length); 
    }

    public object Current
    { 
        get
        {
            try
            {
                return this._list._objects[_currentIndex];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }

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

Вам также необходимо удалить private из объявления _objects в List классе.И передайте this конструктору ListEnumerator.

public class List : IEnumerable
{
    object[] _objects;

    public List()
    {
        _objects = new object[100];
    }
    //Other code of List class.

    public IEnumerator GetEnumerator()
    {
       return new ListEnumerator(this);
    }
}

Подход 2

Измените ListEnumerator конструктор класса так, чтобы он принимал массив объектов.

private class ListEnumerator : IEnumerator 
{
    private object[] _objects;
    public ListEnumerator(object[] objects)
    {
        this._objects = objects;
    }

    private int _currentIndex = -1; 

    public bool MoveNext()
    {
        _currentIndex++;

        return (_currentIndex < this._objects.Length); 
    }

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

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

И используйте его следующим образом в классе List.

public IEnumerator GetEnumerator()
{
   return new ListEnumerator(this._objects);
} 
...