Оператор ИЛИ в C#
-
05-07-2019 - |
Вопрос
Могу ли я достичь
if (a == "b" || "c")
вместо
if (a == "b" || a== "c")
?
Решение
Нет, вы можете сделать:
if (new[] { "b", "c" }.Contains(a))
если у вас есть ЛИНК доступны расширения, но это вряд ли улучшение.
В ответ на комментарий о производительности приведем базовый код синхронизации.Обратите внимание, что к коду необходимо относиться критически, я мог сделать здесь что-то, что исказило бы тайминги.
Сначала результаты:
||, not found: 26 ms
||, found: 8 ms
array.Contains, not found: 1407 ms
array.Contains, found: 1388 ms
array.Contains, inline array, not found: 1456 ms
array.Contains, inline array, found: 1427 ms
switch-statement, not interned, not found: 26 ms
switch-statement, not interned, found: 14 ms
switch-statement, interned, not found: 25 ms
switch-statement, interned, found: 8 ms
Весь код выполнялся дважды, и только передал номер nr.2, чтобы удалить из уравнения накладные расходы JITting.Оба прохода выполняли каждый тип проверки один миллион раз, и выполняли его как там, где искомый элемент был одним из элементов, в которых его нужно найти (то есть оператор if выполнил бы свой блок), так и один раз, когда элемент не был (блок не будет выполнен).Сообщается время каждого из них.Я тестировал как предварительно созданный массив, так и тот, который создается каждый раз, в этой части я не уверен, насколько компилятор выводит и оптимизирует, здесь может быть ошибка.
В любом случае оказывается, что использование оператора переключателя, с интернированием строки или без него, дает примерно те же результаты, что и простой оператор или, чего и следовало ожидать, тогда как поиск в массиве является гораздо более затратным, что для меня это было также ожидаемо.
Пожалуйста, поработайте с кодом и исправьте (или прокомментируйте) его, если возникнут проблемы.
А вот исходный код, довольно длинный:
using System;
using System.Linq;
using System.Diagnostics;
namespace StackOverflow826081
{
class Program
{
private const Int32 ITERATIONS = 1000000;
static void Main()
{
String a;
String[] ops = CreateArray();
Int32 count;
Stopwatch sw = new Stopwatch();
Int32 pass = 0;
Action<String, Int32> report = delegate(String title, Int32 i)
{
if (pass == 2)
Console.Out.WriteLine(title + ": " + sw.ElapsedMilliseconds + " ms");
};
for (pass = 1; pass <= 2; pass++)
{
#region || operator
a = "a";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (a == "b" || a == "c")
{
count++;
}
}
sw.Stop();
report("||, not found", count);
sw.Reset();
a = "b";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (a == "b" || a == "c")
{
count++;
}
}
sw.Stop();
report("||, found", count);
sw.Reset();
#endregion
#region array.Contains
a = "a";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (ops.Contains(a))
{
count++;
}
}
sw.Stop();
report("array.Contains, not found", count);
sw.Reset();
a = "b";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (ops.Contains(a))
{
count++;
}
}
sw.Stop();
report("array.Contains, found", count);
sw.Reset();
#endregion
#region array.Contains
a = "a";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (CreateArray().Contains(a))
{
count++;
}
}
sw.Stop();
report("array.Contains, inline array, not found", count);
sw.Reset();
a = "b";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
if (CreateArray().Contains(a))
{
count++;
}
}
sw.Stop();
report("array.Contains, inline array, found", count);
sw.Reset();
#endregion
#region switch-statement
a = GetString().Substring(0, 1); // avoid interned string
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
switch (a)
{
case "b":
case "c":
count++;
break;
}
}
sw.Stop();
report("switch-statement, not interned, not found", count);
sw.Reset();
a = GetString().Substring(1, 1); // avoid interned string
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
switch (a)
{
case "b":
case "c":
count++;
break;
}
}
sw.Stop();
report("switch-statement, not interned, found", count);
sw.Reset();
#endregion
#region switch-statement
a = "a";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
switch (a)
{
case "b":
case "c":
count++;
break;
}
}
sw.Stop();
report("switch-statement, interned, not found", count);
sw.Reset();
a = "b";
sw.Start();
count = 0;
for (Int32 index = 0; index < ITERATIONS; index++)
{
switch (a)
{
case "b":
case "c":
count++;
break;
}
}
sw.Stop();
report("switch-statement, interned, found", count);
sw.Reset();
#endregion
}
}
private static String GetString()
{
return "ab";
}
private static String[] CreateArray()
{
return new String[] { "b", "c" };
}
}
}
Другие советы
Ну, самое близкое к этому, что вы можете получить, это:
switch (a) {
case "b":
case "c":
// variable a is either "b" or "c"
break;
}
Насколько мне известно, это не вариант.
Вы можете использовать регулярные выражения:
if(Regex.IsMatch(a, "b|c"))
Если содержимое «a» может быть длиннее одного символа, используйте это:
if(Regex.IsMatch(a, "^(b|c)$"))
Нет, не с таким синтаксисом.Но есть много вариантов это закодировать.
if ("bc".Contains(a)) { } // Maybe check a.Length == 1, too.
if ((a[0] & 0x62) == 0x62) { } // Maybe check a.Length == 1, too.
if (new String[] { "b", "c" }.Contains(a)) { }
Возможно, вы могли бы выполнить некоторую перегрузку операторов и заставить свой синтаксис работать, но это действительно зависит от того, чего вы хотите достичь, и это трудно сказать на вашем простом примере.
Можно в определенных ситуациях.А именно, помеченные перечисления:
[Flags]
enum MyEnum {
None = 0,
A = 1,
B = 2,
C = 4,
D = 8
}
//...
MyEnum a = MyEnum.B
if((a & (MyEnum.B | MyEnum.C)) > 0)
// do something
эквивалентно :
if((a & MyEnum.B) > 0 || (a & MyEnum.C) > 0)
// do something
Причина этого связана с битовыми масками.В двоичном формате
None = 00000
A = 00001
B = 00010
C = 00100
D = 01000
Поэтому, когда мы используем | Оператор, мы проводим немного сравнения в поисках любых 1 в столбце и копируем их в результате.Если в столбце нет единиц, вы копируете 0.
B 00010
& C 00100
---------
00110
Затем, когда мы применяем оператор &, мы ищем единицы во всех строках каждого столбца, прежде чем копировать 1.
(B & C) 00110
& (a = B) 00010
---------------
00010
Это > 0, что возвращает true.
Как ни странно, это наиболее эффективный способ сделать это, поскольку он экономит числовое сравнение (>) и логический оператор (||), который выполняет все эти замысловатые короткие замыкания и тому подобное.
Нет, в C# оператор or (||) работает иначе.
Альтернативное решение, хотя и делает код менее читабельным, — создать функцию, которая проверяет нужное вам значение, что-то вроде:
public static bool Any(object a, params object[] b)
{
foreach(object item in b)
{
if(a == b)
{
return true;
}
}
return false;
}