Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Побитовые и сменные операторы включают унарное битовое дополнение, двоичное и правое сдвиг, без знака вправо и двоичные логические И, OR и эксклюзивные операторы OR. Эти операторы принимают операнды целочисленных числовых типов или типа char .
- Унарный оператор
~(побитовое дополнение) - Операторы binary
<<(shift),>>и>>>(без знака вправо) - Бинарные операторы
&(логическое и),|(логическое или) и^(логическое исключающее или)
Эти операторы определены для типов int, uint, long, ulong, nint и nuint. Если оба операнда имеют другие целочисленные типы (sbyte, byte, short, ushort или char), их значения преобразуются в тип int, который также является типом результата операции. Если операнды имеют разные целочисленные типы, их значения преобразуются в ближайший содержащий целочисленный тип. Дополнительные сведения см. в разделе Числовые повышения уровня в статье Спецификации языка C#. Составные операторы (например >>=) не преобразовывают свои аргументы int в тип результата или имеют тип результата в виде int.
Операторы &, | и ^ также определены для операндов типа bool. Дополнительные сведения см. в разделе Логические операторы.
Побитовые операции и операции сдвига никогда не вызывают переполнение и дают одинаковые результаты в проверенных и непроверенных контекстах.
Оператор побитового дополнения ~
Оператор ~ создает побитовое дополнение своего операнда путем инвертирования каждого бита:
uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011
Можно также использовать символ ~ для объявления методов завершения. Дополнительные сведения см. в разделе Методы завершения.
Оператор сдвига влево <<
Оператор << сдвигает левый операнд влево на количество битов, определенное правым операндом. Сведения о том, как правый операнд определяет величину сдвига, см. в разделе Величина смещения операторов сдвига.
Операция сдвига влево отбрасывает старшие биты, которые находятся за пределами диапазона типа результата, и задает позиции пустых битов низкого порядка, равные нулю, как показано в следующем примере:
uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");
uint y = x << 4;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After: 10010000000000000000000100010000
Поскольку операторы сдвига определены только для типов int, uint, long и ulong, результат операции всегда содержит по крайней мере 32 бита. Если левый операнд имеет другой целочисленный тип (sbyte, byte, short, ushort или char), его значение преобразуется в тип int, как показано в следующем примере:
byte a = 0b_1111_0001;
var b = a << 8;
Console.WriteLine(b.GetType());
Console.WriteLine($"Shifted byte: {Convert.ToString(b, toBase: 2)}");
// Output:
// System.Int32
// Shifted byte: 1111000100000000
Оператор shift вправо >>
Оператор >> сдвигает левый операнд вправо на количество битов, определенное правым операндом. Сведения о том, как правый операнд определяет величину сдвига, см. в разделе Величина смещения операторов сдвига.
Операция сдвига вправо удаляет младшие разряды, как показано в следующем примере:
uint x = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2), 4}");
uint y = x >> 2;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2).PadLeft(4, '0'), 4}");
// Output:
// Before: 1001
// After: 0010
Позиции пустых битов высокого порядка задаются с учетом типа левого операнда следующим образом:
Если левый операнд имеет тип
intилиlong, оператор сдвига вправо выполняет арифметический сдвиг, то есть значение наиболее значимого бита (знаковый бит) левого операнда переносится в пустые битовые позиции высокого разряда. То есть для пустых битовых позиций высокого порядка задается ноль, если левый операнд неотрицательный, и единица, если он отрицательный.int a = int.MinValue; Console.WriteLine($"Before: {Convert.ToString(a, toBase: 2)}"); int b = a >> 3; Console.WriteLine($"After: {Convert.ToString(b, toBase: 2)}"); // Output: // Before: 10000000000000000000000000000000 // After: 11110000000000000000000000000000Если левый операнд имеет тип
uintилиulong, оператор сдвига вправо выполняет логический сдвиг, то есть пустым битовым позициям высокого порядка всегда присваивается нулевое значение.uint c = 0b_1000_0000_0000_0000_0000_0000_0000_0000; Console.WriteLine($"Before: {Convert.ToString(c, toBase: 2), 32}"); uint d = c >> 3; Console.WriteLine($"After: {Convert.ToString(d, toBase: 2).PadLeft(32, '0'), 32}"); // Output: // Before: 10000000000000000000000000000000 // After: 00010000000000000000000000000000
Примечание.
Используйте оператор без знака вправо, чтобы выполнить логическую смену операндов со знаком целочисленных типов. Логический сдвиг предпочтителен. Избегайте приведения левого операнда к типу без знака, а затем приведение результата операции смены обратно к подписанному типу.
Оператор unsigned right-shift >>>
Оператор >>> сдвигает левый операнд вправо на количество битов, определенное правым операндом. Сведения о том, как правый операнд определяет величину сдвига, см. в разделе Величина смещения операторов сдвига.
Оператор >>> всегда выполняет логический сдвиг. То есть пустые позиции высокого порядка всегда задаются нулю, независимо от типа левого операнда. Оператор >> выполняет арифметический сдвиг (т. е. значение наиболее значимых битов распространяется на пустые позиции высокого порядка), если левый операнд имеет подписанный тип. В следующем примере показано различие между >> операторами и >>> отрицательным левым операндом:
int x = -8;
Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2), 32}");
int y = x >> 2;
Console.WriteLine($"After >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2), 32}");
int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'), 32}");
// Output:
// Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000
// After >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110
// After >>>: 1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110
Логический оператор AND &
Оператор & вычисляет побитовое логическое И целочисленных операндов:
uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000
Для операндов типа bool оператор & вычисляет логическое И по своим операндам. Унарный оператор & является оператором AddressOf.
Оператор логического исключения ИЛИ ^
Оператор ^ вычисляет побитовое логическое исключающее ИЛИ, также известное как побитовое логическое XOR, своих целочисленных операндов:
uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100
Для операндов типа bool оператор ^ вычисляет логическое исключающее ИЛИ по своим операндам.
Оператор логического ИЛИ |
Оператор | вычисляет побитовое логическое ИЛИ своих целочисленных операндов:
uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001
Для операндов типа bool оператор | вычисляет логическое ИЛИ по своим операндам.
Составное присваивание
Для бинарного оператора op выражение составного присваивания в форме
x op= y
Эквивалентен
x = x op y
За исключением того, что x оценивается только один раз.
В следующем примере показано использование составного присваивания с побитовыми операторами и операторами сдвига:
uint INITIAL_VALUE = 0b_1111_1000;
uint a = INITIAL_VALUE;
a &= 0b_1001_1101;
Display(a); // output: 10011000
a = INITIAL_VALUE;
a |= 0b_0011_0001;
Display(a); // output: 11111001
a = INITIAL_VALUE;
a ^= 0b_1000_0000;
Display(a); // output: 01111000
a = INITIAL_VALUE;
a <<= 2;
Display(a); // output: 1111100000
a = INITIAL_VALUE;
a >>= 4;
Display(a); // output: 00001111
a = INITIAL_VALUE;
a >>>= 4;
Display(a); // output: 00001111
void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2).PadLeft(8, '0'), 8}");
Из-за восходящих приведений результат операции op может быть невозможно неявно преобразовать в тип T из x. В этом случае, если op является предопределенным оператором, и результат операции является явно преобразуемым в тип Tx, выражение составного присваивания формы x op= y эквивалентно x = (T)(x op y), за исключением того, что x вычисляется только один раз. В следующем примере продемонстрировано такое поведение.
byte x = 0b_1111_0001;
int b = x << 8;
Console.WriteLine($"{Convert.ToString(b, toBase: 2)}"); // output: 1111000100000000
x <<= 8;
Console.WriteLine(x); // output: 0
Приоритет операторов
Следующий список упорядочивает побитовые операторы и операторы сдвига по приоритету, от высокого до низкого:
- Оператор побитового дополнения
~ - Операторы
<<shift ,>>и>>> - Оператор логического И
& - Оператор логического исключающего ИЛИ
^ - Оператор логического ИЛИ
|
Порядок вычисления, определяемый приоритетом операторов, можно изменить с помощью скобок (()).
uint a = 0b_1101;
uint b = 0b_1001;
uint c = 0b_1010;
uint d1 = a | b & c;
Display(d1); // output: 1101
uint d2 = (a | b) & c;
Display(d2); // output: 1000
void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2), 4}");
Полный список операторов C#, упорядоченный по уровню приоритета, можно найти в разделе Приоритет операторов статьи Операторы C#.
Величина смещения операторов сдвига
x << count
x >> countДля выражений и x >>> count выражений фактическое число сдвигов зависит от типа x следующим образом:
Если тип
xявляетсяintилиuint, младшие пять битов правого операнда определяют количество сдвигов. То есть величина сдвига вычисляется на основеcount & 0x1F(илиcount & 0b_1_1111).Если тип
xявляетсяlongилиulong, то нижайшие шесть бит правого операнда определяют число сдвигов. То есть величина сдвига вычисляется на основеcount & 0x3F(илиcount & 0b_11_1111).
В следующем примере продемонстрировано такое поведение.
int count1 = 0b_0000_0001;
int count2 = 0b_1110_0001;
int a = 0b_0001;
Console.WriteLine($"{a} << {count1} is {a << count1}; {a} << {count2} is {a << count2}");
// Output:
// 1 << 1 is 2; 1 << 225 is 2
int b = 0b_0100;
Console.WriteLine($"{b} >> {count1} is {b >> count1}; {b} >> {count2} is {b >> count2}");
// Output:
// 4 >> 1 is 2; 4 >> 225 is 2
int count = -31;
int c = 0b_0001;
Console.WriteLine($"{c} << {count} is {c << count}");
// Output:
// 1 << -31 is 2
Примечание.
Как показано в предыдущем примере, результат операции сдвига может быть ненулевым, даже если значение правого операнда больше числа битов в левом операнде.
Enumeration logical operators (Логические операторы перечисления)
Каждый тип перечисления поддерживает операторы ~, &, |, и ^. Для операндов одного типа перечисления логическая операция выполняется по соответствующим значениям базового целочисленного типа. Например, для любого x и y типа перечисления T с базовым типом U выражение x & y дает тот же результат, что и выражение (T)((U)x & (U)y).
Побитовые логические операторы обычно используются с типом перечисления, который определяется с помощью атрибута Flags. Дополнительные сведения см. в разделе Типы перечислений как битовые флаги в статье Типы перечислений.
Возможность перегрузки оператора
Определяемый пользователем тип может перегружать~операторы , <<, >>>>>, &а также |^ операторы. При перегрузке бинарного оператора соответствующий оператор составного присваивания также неявно перегружается. Начиная с C# 14, определяемый пользователем тип может явно перегружать операторы составного назначения, чтобы обеспечить более эффективную реализацию. Как правило, тип перегружает эти операторы, так как значение можно обновить на месте, а не распределить новый экземпляр для хранения результата двоичной операции. Если тип не предоставляет явной перегрузки, компилятор создает неявную перегрузку.
Если определяемый пользователем тип перегружает Tоператор или <<>> оператор, тип левого операнда должен быть>>>.T В C# 10 и более ранних версиях тип правого операнда должен быть int; тип перегружаемого оператора смены вправо может быть любым.
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:
- Оператор побитового дополнения
- Операторы сдвига
- Логические операторы
- Составное присваивание
- Восходящие приведения числовых типов
- Расслабленные требования к смене
- Логический оператор shift вправо
- Определяемое пользователем составное назначение