Объявление переменных в ДЛЯ циклов

Вы можете подойти, как на следующем примере.

Объяснение: unary plus используется для приведения логического числа к числам, если результат разности этих логических значений равен zero, то используйте сравнение по полю name с [114 ] localeCompare () .

const originalAttributes = [
    {name: "P", required: false},
    {name: "D", required: true},
    {name: "W", required: true},
    {name: "A", required: true},
    {name: "N", required: false},
    {name: "S", required: false},
    {name: "SI", required: false}
];

let res = originalAttributes.sort((a, b) =>
    (+b.required - +a.required) || a.name.localeCompare(b.name)
);

const listNames = a => a.map(
    i => `${i.name} ${i.required ? '--R' : ''}`
).join("\n");

console.log(listNames(res));

5
задан GEOCHET 1 June 2009 в 19:31
поделиться

7 ответов

Большую часть времени не имеет значения, объявляете ли Вы переменную внутри или снаружи цикла; правила определенного присвоения гарантируют, что оно не имеет значения. В отладчике Вы могли бы иногда видеть старые значения (т.е. если Вы смотрите на переменную в точке останова, прежде чем он будет присвоен), но статический анализ доказывает, что это не повлияет на выполняющий код. Переменные никогда не сбрасываются на цикл, поскольку нет очевидно никакой потребности.

На уровне IL ** обычно* переменная объявляется только однажды для метода - размещение в цикле является просто удобством для нас программисты.

ОДНАКО существует важное исключение; любое время переменная получена, правила обзора данных, становится более сложным. Например (2 secs):

        int value;
        for (int i = 0; i < 5; i++)
        {
            value = i;
            ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(value); });
        }
        Console.ReadLine();

Очень отличается от:

        for (int i = 0; i < 5; i++)
        {
            int value = i;
            ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(value); });
        }
        Console.ReadLine();

Поскольку "значение" во втором примере действительно на экземпляр, так как это получено. Это означает, что первый пример мог бы показать (например), "4 4 4 4 4", тогда как второй пример покажет 0-5 (в любом порядке) - т.е. "1 2 5 3 4".

Так: получения были вовлечены в исходный код? Что-либо с лямбдой, анонимным методом или запросом LINQ квалифицировало бы.

15
ответ дан 18 December 2019 в 05:23
поделиться

Сводка

Сравнение сгенерированного IL для объявления переменных в цикле к сгенерированному IL для объявления переменных вне цикла доказывает, что нет никакого различия в производительности между двумя стилями объявления переменной. (Сгенерированный IL фактически идентичен.)


Вот первоисточник, предположительно, с помощью "больше ресурсов", потому что переменные объявляются в цикле:

using System;

class A
{
    public static void Main()
    {
        for (int i =0; i< 10; i++)
        {
            decimal? testDecimal;
            string testString;
            switch( i % 2  )
            {
                case 0:
                    testDecimal = i / ( decimal ).32;
                    testString = i.ToString();
                    break;
                default:
                    testDecimal = null;
                    testString = null;
                    break;
            }

            Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
        }
    }
}

Вот IL из неэффективного источника объявления:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    .locals init (
        [0] int32 num,
        [1] valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal> nullable,
        [2] string str,
        [3] int32 num2,
        [4] bool flag)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: br.s L_0061
    L_0005: nop 
    L_0006: ldloc.0 
    L_0007: ldc.i4.2 
    L_0008: rem 
    L_0009: stloc.3 
    L_000a: ldloc.3 
    L_000b: ldc.i4.0 
    L_000c: beq.s L_0010
    L_000e: br.s L_0038
    L_0010: ldloca.s nullable
    L_0012: ldloc.0 
    L_0013: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
    L_0018: ldc.i4.s 0x20
    L_001a: ldc.i4.0 
    L_001b: ldc.i4.0 
    L_001c: ldc.i4.0 
    L_001d: ldc.i4.2 
    L_001e: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    L_0023: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Division(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
    L_0028: call instance void [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>::.ctor(!0)
    L_002d: nop 
    L_002e: ldloca.s num
    L_0030: call instance string [mscorlib]System.Int32::ToString()
    L_0035: stloc.2 
    L_0036: br.s L_0044
    L_0038: ldloca.s nullable
    L_003a: initobj [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>
    L_0040: ldnull 
    L_0041: stloc.2 
    L_0042: br.s L_0044
    L_0044: ldstr "Loop {0}: testDecimal={1} - testString={2}"
    L_0049: ldloc.0 
    L_004a: box int32
    L_004f: ldloc.1 
    L_0050: box [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>
    L_0055: ldloc.2 
    L_0056: call void [mscorlib]System.Console::WriteLine(string, object, object, object)
    L_005b: nop 
    L_005c: nop 
    L_005d: ldloc.0 
    L_005e: ldc.i4.1 
    L_005f: add 
    L_0060: stloc.0 
    L_0061: ldloc.0 
    L_0062: ldc.i4.s 10
    L_0064: clt 
    L_0066: stloc.s flag
    L_0068: ldloc.s flag
    L_006a: brtrue.s L_0005
    L_006c: ret 
}

Вот источник, объявляя переменные вне цикла:

using System;

class A
{
    public static void Main()
    {
        decimal? testDecimal;
        string testString;

        for (int i =0; i< 10; i++)
        {
            switch( i % 2  )
            {
                case 0:
                    testDecimal = i / ( decimal ).32;
                    testString = i.ToString();
                    break;
                default:
                    testDecimal = null;
                    testString = null;
                    break;
            }

            Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
        }
    }
}

Вот является IL объявлением переменных вне цикла:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    .locals init (
        [0] valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal> nullable,
        [1] string str,
        [2] int32 num,
        [3] int32 num2,
        [4] bool flag)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.2 
    L_0003: br.s L_0061
    L_0005: nop 
    L_0006: ldloc.2 
    L_0007: ldc.i4.2 
    L_0008: rem 
    L_0009: stloc.3 
    L_000a: ldloc.3 
    L_000b: ldc.i4.0 
    L_000c: beq.s L_0010
    L_000e: br.s L_0038
    L_0010: ldloca.s nullable
    L_0012: ldloc.2 
    L_0013: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
    L_0018: ldc.i4.s 0x20
    L_001a: ldc.i4.0 
    L_001b: ldc.i4.0 
    L_001c: ldc.i4.0 
    L_001d: ldc.i4.2 
    L_001e: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    L_0023: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Division(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
    L_0028: call instance void [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>::.ctor(!0)
    L_002d: nop 
    L_002e: ldloca.s num
    L_0030: call instance string [mscorlib]System.Int32::ToString()
    L_0035: stloc.1 
    L_0036: br.s L_0044
    L_0038: ldloca.s nullable
    L_003a: initobj [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>
    L_0040: ldnull 
    L_0041: stloc.1 
    L_0042: br.s L_0044
    L_0044: ldstr "Loop {0}: testDecimal={1} - testString={2}"
    L_0049: ldloc.2 
    L_004a: box int32
    L_004f: ldloc.0 
    L_0050: box [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal>
    L_0055: ldloc.1 
    L_0056: call void [mscorlib]System.Console::WriteLine(string, object, object, object)
    L_005b: nop 
    L_005c: nop 
    L_005d: ldloc.2 
    L_005e: ldc.i4.1 
    L_005f: add 
    L_0060: stloc.2 
    L_0061: ldloc.2 
    L_0062: ldc.i4.s 10
    L_0064: clt 
    L_0066: stloc.s flag
    L_0068: ldloc.s flag
    L_006a: brtrue.s L_0005
    L_006c: ret 
}

Я совместно использую секрет, за исключением порядка в который .locals init ( ... ) указаны, IL является точно тем же. ОБЪЯВЛЕНИЕ переменных в цикле не приводит ни к КАКОМУ ДОПОЛНИТЕЛЬНОМУ IL.

14
ответ дан 18 December 2019 в 05:23
поделиться

Вы не должны вставлять объявления для цикла так или иначе. Это сосет дополнительные ресурсы для создания переменной много раз, когда то, что необходимо сделать, просто очистить переменную с каждым повторением.

Нет, это не делает! Полная противоположность Вашего совета должна быть сделана. Но даже если было более эффективно сбросить переменную, это намного более ясно объявить переменную в своем самом трудном объеме. И ясность выигрывает микрооптимизацию (почти) любое время. Кроме того, одна переменная, одно использование. Не снова используйте переменные излишне.

Тем не менее переменные не сбрасываются, ни повторно инициализируются здесь – на самом деле, они даже не инициализируются C#! Для фиксации этого просто инициализируйте их и быть сделанными.

8
ответ дан 18 December 2019 в 05:23
поделиться

Вот вывод Вашего кода:

Loop 0: testDecimal=0 - testString=0
Loop 1: testDecimal= - testString=
Loop 2: testDecimal=6.25 - testString=2
Loop 3: testDecimal= - testString=
Loop 4: testDecimal=12.5 - testString=4
Loop 5: testDecimal= - testString=
Loop 6: testDecimal=18.75 - testString=6
Loop 7: testDecimal= - testString=
Loop 8: testDecimal=25 - testString=8
Loop 9: testDecimal= - testString=

Я ничего не изменил в Вашем отправленном источнике для генерации этого вывода. Обратите внимание, что это не выдает исключение также.

2
ответ дан 18 December 2019 в 05:23
поделиться

что-то странное продолжается здесь, если они никогда не инициализируются, затем оно должно бросить ошибку компиляции.

когда я выполнил Ваш код, я получил точно, что я буду ожидать, ничто на нечетных циклах и правильные числа на ровных циклах.

0
ответ дан 18 December 2019 в 05:23
поделиться

Вы получали ошибку NullReferenceException?

Из кода выше Вас получил бы ту ошибку на каждом нечетном повторении цикла, поскольку Вы пытаетесь распечатать переменные после того, как Вы назначили им пустым указателем.

0
ответ дан 18 December 2019 в 05:23
поделиться

Это делает удивил меня также. Я думал бы, что объем изменится в “для” цикла. Это, кажется, не имеет место. Значения сохраняются. Компилятор, кажется, достаточно умен для объявления в переменный один раз, когда “для” цикла сначала вводится.

Я действительно соглашаюсь с предыдущими сообщениями, что Вы не должны вставлять объявления “для” цикла так или иначе. При инициализации переменной, Вы будете неопытными ресурсы на каждом цикле.

Но если Вы повреждаете внутреннюю часть “для” цикла к функции (я знаю, что это все еще плохо). Вы выходите из объема, и переменные создаются каждый раз.

private void LoopTest()
{
    for (int i =0; i< 10; i++)
    {
        DoWork(i);
    }
}

private void Work(int i)
{
    decimal? testDecimal;
    string testString;

    switch (i % 2)
    {
        case 0:
            testDecimal = i / (decimal).32;
            testString = i.ToString();
            break;
        default:
            testDecimal = null;
            testString = null;
            break;
    }
    Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
}

Хорошо, по крайней мере, я изучил что-то новое. А также как плохо переменная объявления в циклах действительно.

-1
ответ дан 18 December 2019 в 05:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: