:: Не фоксом единым
static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Есть код, всё работает.

class SampleEnumerator
{
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
yield return 3;
}
}
class Program
{
static void Main(string[] args)
{
foreach (int number in new SampleEnumerator())
{
Console.Write($"{number.ToString()} ");
}
Console.ReadLine();
}
}

Хочу сделать класс SampleEnumerator статическим. Делаю:

static class StaticEnumerator
{
public static IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
yield return 3;
}
}
class Program
{
static void Main(string[] args)
{
//foreach (int number in StaticEnumerator.GetEnumerator()) // так некорректно
foreach (int number in StaticEnumerator) // тут ошибка
{
Console.Write($"{number.ToString()} ");
}
Console.ReadLine();
}
}

Цитата:
Оператор foreach не работает с переменными типа "System.Collections.Generic.IEnumerator<int>", так как "System.Collections.Generic.IEnumerator<int>" не содержит открытого определения для "GetEnumerator".

Вопрос - чего не хватает? Что такое "открытое определения для GetEnumerator"?

Цитата:
'StaticEnumerator" является тип, который недопустим в данном контексте.

Вопрос - чего не хватает?



Исправлено 3 раз(а). Последнее : S-type, 26.02.17 22:37
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
В то же время, если сделать:

static class StaticSample
{
//public static IEnumerable<int> GetEnumerator()
public static IEnumerable<int> MyFunc()
{
yield return 1;
yield return 2;
yield return 3;
}
}
class Program
{
static void Main(string[] args)
{
// foreach (int number in StaticSample.GetEnumerator())
foreach (int number in StaticSample.MyFunc())
{
Console.Write($"{number.ToString()} ");
}
Console.ReadLine();
}
}

всё работает "как надо". Но, если попробовать: Вот так работает:

class StaticSample
{
//public IEnumerable<int> GetEnumerator()
public IEnumerable<int> MyFunc()
{
yield return 1;
yield return 2;
yield return 3;
}
}
class Program
{
static void Main(string[] args)
{
//foreach (int number in new StaticSample()) // тут ошибка
foreach (int number in new StaticSample().MyFunc()) // надо так
{
Console.Write($"{number.ToString()} ");
}
Console.ReadLine();
}
}

Цитата:
Оператор foreach требует, чтобы возвращаемый тип "IEnumerable<int>" для "StaticSample.GetEnumerator()" имел соответствующий открытый метод MoveNext и открытое свойство Current

Только что не требовал, а тут вдруг понадобилось? Вопрос - почему? Потому, что не доглядел.



Исправлено 4 раз(а). Последнее : S-type, 26.02.17 22:13
Ratings: 0 negative/0 positive
Re: static IEnumerator
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
S-type

Цитата:
Оператор foreach требует, чтобы возвращаемый тип "IEnumerable<int>" для "StaticSample.GetEnumerator()" имел соответствующий открытый метод MoveNext и открытое свойство Current

Только что не требовал, а тут вдруг понадобилось? Вопрос - почему?

1) StaticSample.GetEnumerator().GetEnumerator() - честный IEnumerator

2) new StaticSample().GetGnumerator() - возвращает IEnumerable<int> который не похож на IEnumerator, о чем намекает сообщение.

IEnumerable (последовательность) и IEnumerator (указатель на текущий элемент последовательности) не одно и то же.



Исправлено 1 раз(а). Последнее : Рома, 26.02.17 20:07
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Рома
1) StaticSample.GetEnumerator().GetEnumerator() - честный IEnumerator

Спасибо за подсказку. Подкорректировал выложенный мной пример.
Ratings: 0 negative/0 positive
Re: static IEnumerator
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Минимальный пример - статический метод в том же самом классе.
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
{
static class Program
{
static void Main(string[] args)
{
foreach (var item in MyCollection())
Console.WriteLine(item);
Console.ReadKey();
}
static IEnumerable<int> MyCollection()
{
yield return 1;
yield return 2;
yield return 3;
}
}
}

На самом деле этот безобидный код компилируется в весьма нехилую структуру с полноценной state-машиной:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ConsoleApplication2
{
internal static class Program
{
private static void Main(string[] args)
{
foreach (int item in Program.MyCollection())
{
Console.WriteLine(item);
}
Console.ReadKey();
}
[IteratorStateMachine(typeof(Program.<MyCollection>d__1))]
private static IEnumerable<int> MyCollection()
{
return new Program.<MyCollection>d__1(-2);
}
[CompilerGenerated]
private sealed class <MyCollection>d__1 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IDisposable, IEnumerator
{
[DebuggerHidden]
public <MyCollection>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
bool IEnumerator.MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
this.<>2__current = 2;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;
case 3:
this.<>1__state = -1;
return false;
default:
return false;
}
}
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
Program.<MyCollection>d__1 result;
if (this.<>1__state == -2 && this.<>l__initialThreadId == Environment.CurrentManagedThreadId)
{
this.<>1__state = 0;
result = this;
}
else
{
result = new Program.<MyCollection>d__1(0);
}
return result;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
void IDisposable.Dispose()
{
}
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
private int <>1__state;
private int <>2__current;
private int <>l__initialThreadId;
}
}
}

Мануал про всё это пишет достаточно подробно, даже про Technical Implementation...
MSDN
Although you write an iterator as a method, the compiler translates it into a nested class that is, in effect, a state machine. This class keeps track of the position of the iterator as long the foreach loop in the client code continues.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Таки прозвучало слово Итератор... Что же это такое? В приведённой ссылке сказано:

Цитата:
An iterator can be used to step through collections such as lists and arrays.

В старой версии страницы есть русский язык msdn.microsoft.com , там сказано:

Цитата:
Итератор можно использовать для прохода по коллекции, такой как список и массив.

Но, самого определения, что такое "итератор" - не даётся.

На msdn.microsoft.com (но, это не для C#, а для C++) сказано

Цитата:
Итератор — это объект, который может выполнять итерацию элементов в контейнере STL и предоставлять доступ к отдельным элементам.

Т.е. итератор, это объект. Но, не каждый объект является итератором, а тот который:

Цитата:
An iterator method or get accessor performs a custom iteration over a collection.

Цитата:
Метод итератора или метод доступа get выполняет настраиваемый проход по коллекции.

Вот тут начинаются непонятки. Покопался, на msdn.microsoft.com сказано:

Цитата:
Тип возвращаемого значения метода итератора или метода доступа get может быть IEnumerable, IEnumerable, IEnumerator или IEnumerator.

Значит, надо различать итератор (объект) и метод итератора. Так?

Выходит, любой метод, возвращающий IEnumerable или IEnumerator это метод итератор (а сам объект - итератор)? Или, какой то другой принцип?

Я правильно понимаю, что метод итератора не обязательно должен содержать yield? И, где здесь get?



Исправлено 2 раз(а). Последнее : S-type, 27.02.17 23:22
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Попробовал посмотреть викиедию ru.wikipedia.org - вообще взорвали мозг

Цитата:
Итератор (от англ. iterator - перечислитель) — интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера) и навигацию по ним.
[...]

Итераторы в .NET Framework называются 'перечислителями' (enumerators) и представлены интерфейсом IEnumerator.



Исправлено 1 раз(а). Последнее : S-type, 27.02.17 23:13
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Вот ещё msdn.microsoft.com

Цитата:
Итератор — метод доступа get или оператор, выполняющий настраиваемую итерацию класса массива или коллекции с помощью ключевого слова yield.
[...]

Итератор — это раздел кода, возвращающий упорядоченную последовательность значений одинакового типа.

Полез глубже в интернет, вижу metanit.com :

Цитата:
Паттерн Итератор (Iterator) предоставляет абстрактный интерфейс для последовательного доступа ко всем элементам составного объекта без раскрытия его внутренней структуры.

Е маё... Что это за зверь такой "итератор"?



Исправлено 1 раз(а). Последнее : S-type, 28.02.17 13:48
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Вот такой пример:

class Enumerator
{
private int i = 5;
public object Current
{
get { return i; }
}
public bool MoveNext()
{
if (i>0)
{
i--;
return true;
}
return false;
}
public Enumerator GetEnumerator()
{
return this;
}
}
class Program
{
static void Main(string[] args)
{
foreach (object o in new Enumerator())
{
Console.WriteLine(o);
}
Console.ReadLine();
}
}

С одной стороны, foreach работает (слава уткам, позор сильной типизации), с другой стороны - IEnumerator тут вовсе упомянут. Понятно, что Current, MoveNext() это из IEnumerator, но интерфейс не реализован - Reset() то отсутствует! Где здесь итератор?



Исправлено 1 раз(а). Последнее : S-type, 27.02.17 23:58
Ratings: 0 negative/0 positive
Re: static IEnumerator
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
S-type
Е маё... Что это за зверь такой "итератор"?
Вот что бывает после чтения википедии

Лучше сразу стандарт языка почитать - там все четко описано.
Например, C++
www.cplusplus.com
An iterator is any object that, pointing to some element in a range of elements (such as an array or a container), has the ability to iterate through the elements of that range using a set of operators (with at least the increment (++) and dereference (*) operators)
В C# то же самое. только вместо increment (++) - MoveNext(), а вместо dereference (*) - Current
То есть, итератор - это указатель на элемент последовательности (я, кстати, выше это уже писал).

Ни в C++, ни в С# не требуется реализации интерфейса. Есть соглашение, что должен иметь объект, чтобы считаться итератором.

Аналогично и для IEnumerable(C#) или Container(C++) есть соглашение
В С# - нужен метод GetEnumerator()
В С++ - нужна пара begin()/end()
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Рома
То есть, итератор - это указатель на элемент последовательности (я, кстати, выше это уже писал).
Ни в C++, ни в С# не требуется реализации интерфейса. Есть соглашение, что должен иметь объект, чтобы считаться итератором.

Т.е. в первой фразе "итератор - это указатель", а во второй "объект"
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Рома
В C# то же самое. только вместо increment (++) - MoveNext(), а вместо dereference (*) - Current
То есть, итератор - это указатель на элемент последовательности (я, кстати, выше это уже писал).

Т.е. в C# итератор, это объект, у которого есть метод MoveNext() и свойство Current. Так?

Выходит, GetEnumerator() возвращает итератор, но сам объект, в котором реализован GetEnumerator(), итератором не является. Так?

Немножко изменю приведённый выше пример:

class Collection
{
public Enumerator GetEnumerator()
{
return new Enumerator();
}
}
class Enumerator
{
private int i = 5;
public object Current
{
get { return i; }
}
public bool MoveNext()
{
if (i > 0)
{
i--;
return true;
}
return false;
}
}
class Program
{
static void Main()
{
foreach (object o in new Collection())
{
Console.WriteLine(o);
}
Console.ReadLine();
}
}

Тут итератор это класс Enumerator? Или, в данном случае итератор "разрезан" на два класса (Enumerator + Collection)?
Ratings: 0 negative/0 positive
Re: static IEnumerator
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Рома
То есть, итератор - это указатель на элемент последовательности (я, кстати, выше это уже писал).
Вот зря, зря употребил термин "указатель"... У него совсем другое значение...
S-type
Т.е. в C# итератор, это объект, у которого есть метод MoveNext() и свойство Current. Так?
Всё же тип а не объект...
S-type
Выходит, GetEnumerator() возвращает итератор, но сам объект, в котором реализован GetEnumerator(), итератором не является. Так?
Так. Он является реализатором интерфейса IEnumerable (при том реализация foreach такова, что собственно "интерфейсы" не используются - по сути как в фоксе - интерфейсом в данном случае служит банально "название метода")
msdn.microsoft.com
Всё описано, включая пример сходный с твоим. И указаны некоторые особенности - про тот же Reset написано, который foreach не использует.
S-type
Тут итератор это класс Enumerator?
Да.
Просто зачастую такой класс делают nested классом в том классе-коллекции которую он обслуживает. В принципе не воспрещается и в самом классе-коллекции соответствующие методы реализовать, возвращая тривиально this из метода GetEnumerator() как в твоём раннем примере, хотя это явное нарушение принципа "единственной ответственности" (S в SOLID).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Igor Korolyov
...явное нарушение принципа "единственной ответственности" (S в SOLID).

IMHO, любая крайность - это опасно. И, если следовать принципу SOLID, то программа теряет "читательность" - очень тяжело лазить по куче классов, "запихивая" их в голову, что бы потом понять - что же этот код делает. S - явно не для меня
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Igor Korolyov
Он является реализатором интерфейса IEnumerable (при том реализация foreach такова, что собственно "интерфейсы" не используются - по сути как в фоксе - интерфейсом в данном случае служит банально "название метода")

Это и называется - утиная типизация. Интересно, где то ещё в C# есть утиная типизация?
Ratings: 0 negative/0 positive
Re: static IEnumerator
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
Igor Korolyov
Рома
То есть, итератор - это указатель на элемент последовательности (я, кстати, выше это уже писал).
Вот зря, зря употребил термин "указатель"... У него совсем другое значение...

"object that, pointing to some element in a range of elements" чем не указатель?
Другое значение - какое?
Если memory pointer, то это частный случай итератора.

И в VFP никого не смущает понятие "record pointer", которое тесно связано с множеством методов итерации по курсору (SKIP,GO,SCAN...)
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Рома
"object that, pointing to some element in a range of elements" чем не указатель?
Другое значение - какое?

Кстати, в ru.wikipedia.org сказано:

Цитата:
Итератор похож на указатель своими основными операциями: он указывает на отдельный элемент коллекции объектов (предоставляет доступ к элементу) и содержит функции для перехода к другому элементу списка (следующему или предыдущему).
Ratings: 0 negative/0 positive
Re: static IEnumerator
S-type
Автор

Сообщений: 2969
Дата регистрации: 24.04.2004
Подытожу. Если что не так - поправляйте.

В программировании итератор — это секция кода (метод), возвращающая упорядоченную последовательность значений одинакового типа.

В C# итератор, это класс, у которого есть:

  • свойство Current - возвращает ссылку на текущий объект.
  • метод MoveNext() - передвигает ссылку на следующий объект и возвращает true, если удалось.

    Классы, реализующие IEnumerator или IEnumerator<T> - это итераторы. Так же, неявно, итератор можно реализовать с помощью IEnumerable, IEnumerable<T>. До появления yield необходимо было реализовывать IEnumerable, а так же описывать отдельный класс для реализации IEnumerator.

    Интерфейс IEnumerable (или IEnumerable<T> ) позволяет использовать в цикле foreach итераторы, реализованные с помощью IEnumerator.



    Исправлено 1 раз(а). Последнее : S-type, 01.03.17 23:27
    Ratings: 0 negative/0 positive
  • Re: static IEnumerator
    alex;

    Сообщений: 2850
    Откуда: Москва
    Дата регистрации: 23.11.2004
    S-type
    В программировании итератор — это секция кода (метод)

    итератор — это интерфейс, т.е. набор свойств и методов доступа и навигации

    не?

    оператор ++ это же метод как бы, и используется для навигации в плюсах точно.



    Исправлено 2 раз(а). Последнее : alex;, 01.03.17 23:54
    Ratings: 0 negative/0 positive
    Re: static IEnumerator
    Igor Korolyov

    Сообщений: 34580
    Дата регистрации: 28.05.2002
    Рома
    "object that, pointing to some element in a range of elements" чем не указатель?
    Другое значение - какое?
    Если memory pointer, то это частный случай итератора.
    В плюсах - возможно (хотя в википедии написано совсем обратное
    Цитата:
    The syntax of standard iterators is designed to resemble that of ordinary C pointer arithmetic, where the * and -> operators are used to reference the element to which the iterator points, and pointer arithmetic operators like ++ are used to advance the iterator to the next element.
    ), в шарпе - скорее нет чем да. Т.к. там Pointer types совершенно конкретно отделены от Iterators. И хотя у указателей есть и * и ++, но это СОВСЕМ другой зверь...
    Опять же, то что имеет с десяток различных вариантов использования (та же pointer arithmetic, сравнение указателей, прямая индексация, небезопасное приведение типов) никак не может быть "частным случаем".
    "Частный случай" это как раз встроенные классы-коллекции реализующие IEnumerable...


    ------------------
    WBR, Igor
    Ratings: 0 negative/0 positive


    Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

    On-line: 13 AndyNigmatec  (Гостей: 12)

    © 2000-2024 Fox Club 
    Яндекс.Метрика