Вы можете подойти, как на следующем примере.
Объяснение: 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));
Большую часть времени не имеет значения, объявляете ли Вы переменную внутри или снаружи цикла; правила определенного присвоения гарантируют, что оно не имеет значения. В отладчике Вы могли бы иногда видеть старые значения (т.е. если Вы смотрите на переменную в точке останова, прежде чем он будет присвоен), но статический анализ доказывает, что это не повлияет на выполняющий код. Переменные никогда не сбрасываются на цикл, поскольку нет очевидно никакой потребности.
На уровне 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 квалифицировало бы.
Сводка
Сравнение сгенерированного 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.
Вы не должны вставлять объявления для цикла так или иначе. Это сосет дополнительные ресурсы для создания переменной много раз, когда то, что необходимо сделать, просто очистить переменную с каждым повторением.
Нет, это не делает! Полная противоположность Вашего совета должна быть сделана. Но даже если было более эффективно сбросить переменную, это намного более ясно объявить переменную в своем самом трудном объеме. И ясность выигрывает микрооптимизацию (почти) любое время. Кроме того, одна переменная, одно использование. Не снова используйте переменные излишне.
Тем не менее переменные не сбрасываются, ни повторно инициализируются здесь – на самом деле, они даже не инициализируются C#! Для фиксации этого просто инициализируйте их и быть сделанными.
Вот вывод Вашего кода:
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=
Я ничего не изменил в Вашем отправленном источнике для генерации этого вывода. Обратите внимание, что это не выдает исключение также.
что-то странное продолжается здесь, если они никогда не инициализируются, затем оно должно бросить ошибку компиляции.
когда я выполнил Ваш код, я получил точно, что я буду ожидать, ничто на нечетных циклах и правильные числа на ровных циклах.
Вы получали ошибку NullReferenceException?
Из кода выше Вас получил бы ту ошибку на каждом нечетном повторении цикла, поскольку Вы пытаетесь распечатать переменные после того, как Вы назначили им пустым указателем.
Это делает удивил меня также. Я думал бы, что объем изменится в “для” цикла. Это, кажется, не имеет место. Значения сохраняются. Компилятор, кажется, достаточно умен для объявления в переменный один раз, когда “для” цикла сначала вводится.
Я действительно соглашаюсь с предыдущими сообщениями, что Вы не должны вставлять объявления “для” цикла так или иначе. При инициализации переменной, Вы будете неопытными ресурсы на каждом цикле.
Но если Вы повреждаете внутреннюю часть “для” цикла к функции (я знаю, что это все еще плохо). Вы выходите из объема, и переменные создаются каждый раз.
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 );
}
Хорошо, по крайней мере, я изучил что-то новое. А также как плохо переменная объявления в циклах действительно.