С# универсальные и неуниверсальные перечислители

Вопрос новичка.

Массивы в C# возвращают не универсальный (классический) IEnumerator. Другие коллекции могут предоставить либо.

В общем, лучше ли использовать общий перечислитель, если он доступен, например, из соображений безопасности типов? Бывают ли случаи, когда необобщенный вариант предпочтительнее?


person Jonathan Allin    schedule 27.06.2012    source источник
comment
Помните, что массивы были в С# 1, а дженерики не появлялись до С# 2, поэтому для массивов (и ArrayList, HashTable и т. д.) было бы сложно их вернуть...   -  person AakashM    schedule 27.06.2012


Ответы (2)


Обычно предпочтительнее использовать общие перечисления, как вы упомянули в вопросе, для безопасности типов.

Неуниверсальное перечисление может быть выбором в случае, когда вы храните только коллекцию объектов. Таким образом, вместо записи IEnumerable<object> вы пишете IEnumerable и определяете реальный тип объекта внутри итерации (если вам это нужно, но обычно да).

person Tigran    schedule 27.06.2012

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.

person Marc Gravell    schedule 27.06.2012