foreach
фактически не всегда используют IEnumerable[<T>]
/IEnumerator[<T>]
API на всех. Во-первых, она на самом деле не требуется: все, что требуется является GetEnumerator()
метод, который возвращает что-то с bool MoveNext()
и Current {get;}
- например, List<T>
имеет пользовательский итератор. Этот подход также был распространен в .NET 1.1, чтобы избежать бокса, отдавая предпочтение пользовательскому итератору.
Однако, в случае массивов это, возможно, еще более интересно; рассмотреть следующие вопросы:
static void Main()
{
foreach(var i in GetData()) Console.WriteLine(i);
}
static int[] GetData()
{
int[] data = { 1, 2, 3, 4, 5 };
return data;
}
Здесь Main
компилируется (//
комментарии мои):
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] int32[] CS$6$0000,
[2] int32 CS$7$0001)
// int[] arr = GetData()
L_0000: call int32[] ConsoleApplication7.Program::GetData()
L_0005: stloc.1
// int j = 0
L_0006: ldc.i4.0
L_0007: stloc.2
// run end-condition of for loop first...
L_0008: br.s L_0018
// i = arr[j]
L_000a: ldloc.1
L_000b: ldloc.2
L_000c: ldelem.i4
L_000d: stloc.0
// Console.WriteLine(i);
L_000e: ldloc.0
L_000f: call void [mscorlib]System.Console::WriteLine(int32)
// j++
L_0014: ldloc.2
L_0015: ldc.i4.1
L_0016: add
L_0017: stloc.2
// j < arr.Length
L_0018: ldloc.2
L_0019: ldloc.1
L_001a: ldlen
L_001b: conv.i4
L_001c: blt.s L_000a
L_001e: ret
}
, который не делает никакой пользы вообще перечислителя API; он фактически реализовал его как цикл for
.
Помните, что массивы были в C# 1, а generics не отображались до C# 2, поэтому было бы сложно использовать массивы (и 'ArrayList',' HashTable' и т. Д.), Чтобы вернуть их ... – AakashM