I think the most common interface for accessing a set of elements is IEnumerable<T> and IEnumerator<T> . Almost all, if not all, containers support it. This pattern is good because it allows access to absolutely any set regardless of its internal storage structure. For example, all T [] arrays, containers from System.Collections.Generic , methods with yield return , etc.
1) However, I was faced with the need to process some sequence of elements with the possibility of going back. Those. for processing the Nth element, it may be necessary to obtain N + m elements. In general, the solution to this problem is to clone the main iterator on the Nth element. The clone is then used only to run up to the N + m element. Then the main iterator continues to work with the Nth on which it stopped. This solution is good because it is very easy to implement. To do this, simply implement the Clone method of the ICloneable interface. Usually this is only 1 line:
public class MyEnumerator<T> : IEnumerator<T>, ICloneable { // ΠΠΎ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΡ
ΠΏΠΎΠ»ΡΡ
ΠΎΠ±ΡΡΠ½ΠΎ Ρ
ΡΠ°Π½ΠΈΡΡΡ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ ΠΈ ΠΈΠ½Π΄Π΅ΠΊΡ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π² Π½Π΅ΠΌ, // Π»ΠΈΠ±ΠΎ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΡΠ²ΡΠ·Π°Π½Π½ΠΎΠΉ ΡΡΡΡΠΊΡΡΡΡ. // Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² IEnumerable<T> ... public object Clone() { // ΠΡΠΎΡΡΠΎΠ΅ ΠΏΠΎΠ²Π΅ΡΡ
Π½ΠΎΡΡΠ½ΠΎΠ΅ ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎΡΠΈΡΠ»Π΅Π½Π½ΡΡ
Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΡ
ΠΏΠΎΠ»Π΅ΠΉ return MemberwiseClone(); } } As an example, the System.CharEnumerator class is described with its use in MSDN and the cloning script.
This solution is very simple to implement, universally and economically. Nevertheless, only 2 cases of its application were found in the vskidku - enumerator in the string (the aforementioned System.CharEnumerator ) and non-typed enumerator in the array:
static void Main(string[] args) { { // Π‘ΡΡΠΎΠΊΠ° ΠΈΠΌΠ΅Π΅Ρ Π΅Π΄ΠΈΠ½ΡΠΉ System.CharEnumerator, ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡΠΈΠΉ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ string str = "123"; IEnumerator<char> enumerator = ((IEnumerable<char>)str).GetEnumerator(); // System.CharEnumerator ICloneable cloneable = (ICloneable)enumerator; // System.CharEnumerator IEnumerator<char> enumerator2 = (IEnumerator<char>)cloneable.Clone(); // System.CharEnumerator } { // IEnumerator<T> Π² ΠΌΠ°ΡΡΠΈΠ²Π°Ρ
ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ char[] arr = new char[3]; IEnumerator<char> enumerator = ((IEnumerable<char>)arr).GetEnumerator(); // System.SZArrayHelper.SZGenericArrayEnumerator<char> //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException } { // IEnumerator Π² ΠΌΠ°ΡΡΠΈΠ²Π°Ρ
ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ char[] arr = new char[3]; IEnumerator enumerator = ((IEnumerable)arr).GetEnumerator(); // System.Array.SZArrayEnumerator ICloneable cloneable = (ICloneable)enumerator; // System.Array.SZArrayEnumerator IEnumerator enumerator2 = (IEnumerator)cloneable.Clone(); // System.Array.SZArrayEnumerator } { // IEnumerator ΠΎΡ List<T> ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ List<char> lst = new List<char>(); IEnumerator enumerator = ((IEnumerable)lst).GetEnumerator(); // System.Collections.Generic.List<char>.Enumerator //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException } { // IEnumerator<T> ΠΎΡ List<T> ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ List<char> lst = new List<char>(); IEnumerator<char> enumerator = ((IEnumerable<char>)lst).GetEnumerator(); // System.Collections.Generic.List<char>.Enumerator //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException } { // IEnumerator<T> Π² yield return ΠΌΠ΅ΡΠΎΠ΄Π΅ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ IEnumerator<char> enumerator = ((IEnumerable<char>)YieldReturnMethod()).GetEnumerator(); // tmp.Program.<YieldReturnMethod>d__1 //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException } { // IEnumerator Π² yield return ΠΌΠ΅ΡΠΎΠ΄Π΅ ΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ IEnumerator enumerator = ((IEnumerable)YieldReturnMethod()).GetEnumerator(); // tmp.Program.<YieldReturnMethod>d__1 //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException } } static IEnumerable<char> YieldReturnMethod() { yield return 'a'; yield return 'b'; yield return 'c'; } The fact of such a small ICloneable support is puzzling. It turns out that almost for everything you have to write your own class that implements IEnumerable<T> and the 2nd class that implements IEnumerator<T> . Probably the most common option would be to pre-retrieve the entire sequence, store it in its container and then use its own set of classes that implement the necessary interfaces. However, this option in some cases may be either inapplicable or very resource-intensive. I would like to ask who thinks about this.