If we have a small look into C# 3.0 language specification we can find this:
- If the type X of expression is an array type then there is an implicit reference conversion from X to the System.Collections.IEnumerable interface (since System.Array implements this interface). The collection type is the System.Collections.IEnumerable interface, the enumerator type is the System.Collections.IEnumerator interface and the element type is the element type of the array type X.
- Otherwise, determine whether the type X has an appropriate GetEnumerator method
The second point is interesting. We can use this and do the following:
public class NotAList
{
private readonly List<string> myInternalList = new () { "Hello", "World"};
public List<string>.Enumerator GetEnumerator() => myInternalList.GetEnumerator();
}
We only have to expose a GetEnumerator
function. Then this piece of code compiles without a problem:
public static class Program
{
public static void Main()
{
var myList = new NotAList();
foreach (string item in myList)
{
Console.Write($"{item} ");
}
}
}
This would print: Hello World
Special case: IEnumerator
IEnumerator
itself has no GetEnumerator
method and therefore if you have a type which only exposes the IEnumerator
- interface you can use a small trick to use them in foreach loops as well. This will only work with C# 9 and higher
- Create an extension method like that (this was not possible before):
public static class Extensions
{
public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator;
}
- Use the extension to provide an enumerator
IEnumerator<string> countryEnumerator = (IEnumerator<string>)new List<string> { "1", "2" };
var enumerator = countryEnumerator.GetEnumerator();
foreach(var item in enumerator)
// ...
For more information look into the official documentation.