Several interrelated issues in one.

Question 1: Suppose there is an indexable collection that also implements access to its elements through an iterator. Is the iterator of such a collection required to return elements in ascending order of the index? Ie, let's say, here is such a collection-wrapper:

class Shuffle<T> : IReadOnlyList<T> { IReadOnlyList<T> _internal; public int Count { get { return _internal.Count; } } public T this[int i] { get { return _internal[i]; } } public Shuffle(IReadOnlyList<T> elements) { if (elements == null) throw new ArgumentNullException(); _internal = elements; } public IEnumerator<T> GetEnumerator() { Random rnd = new Random(); List<int> indexes = new List<int>(_internal.Count); indexes.AddRange(Enumerable.Range(0, _internal.Count)); while (indexes.Count > 0) { int i = rnd.Next(indexes.Count); int ix = indexes[i]; indexes.RemoveAt(i); yield return _internal[ix]; } } IEnumerator IEnumerable.GetEnumerator() { return _internal.GetEnumerator(); } } 

will it violate any generally accepted standards?

Question 2: When working with standard collections, such as T[] or List<T> , if the order of access to the elements of the collection is important , can you, without fear (*), replace such a construction

 char[] ch_arr = new char[] { 'A', 'B', 'C' }; for (int i = 0; i < ch_arr.Length; i++) Console.Write(ch_arr[i]); 

on this:

 char[] ch_arr = new char[] { 'A', 'B', 'C' }; foreach (char c in ch_arr) Console.Write(c); 

?


(*) - the following, for example, script. The kernel update has been released, the memory for the array is no longer allocated in one piece, but can be allocated by blocks, the implementation of the iterator for the array (for speed) has been changed, and the elements can now be returned not in a logical order, but in some arbitrary. Or today we write for one platform, and tomorrow this code needs to be transferred to another, where there is such a feature.

  • But the rules of the site prohibit asking several questions in one post, isn’t it? - Schullz
  • @Schullz, In fact, this is one question, just two parts in it. The answer to one part sheds light on the second and vice versa (at least it seems to me so). - i-one

2 answers 2

In the case of "just an iterator" - no, not required. Moreover, there are collections (from ASP.NET) that list not their own values ​​- but their own keys, which is very unexpected.

But you don’t just have a collection; you have an IReadOnlyList<T> implementation. And for the list, the semantics of these operations is unambiguous (although they forgot to document it explicitly) - the transfer always goes in a row. Thus it works in standard collections - and you need to make your own in a compatible way.

Hence the second point - yes, it is usually safe to replace one cycle with another. I say usually - because the for and foreach cycles have different scope of loop variables.

  • "forgot to document explicitly" - this is alarming. “So it works in standard collections” is still incomprehensible, does it work like that because these are the existing implementations, or because it should be like this (what does the standard, specification, etc., require)? Let's say in .Net implementations of MS and Mono in the array iterators in MoveNext() is index++ . If there is another implementation, where it will be different, will this violate the standards / specifications? I will give an analogy from SQL. There are select and select ... order by ... For some reason, my foreach associated with the 1st, and for with the 2nd. - i-one
  • @ i-one is associated in vain. The list is always ordered. An unordered (more precisely, not necessarily ordered) collection is already an ICollection<> interface - Pavel Mayorov

Concerning the second question:

Resharper suggests replacing for with foreach in such cases. This can be considered as indirect evidence that such a replacement is always admissible.