Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Побитовые и сменные операторы включают унарное битовое дополнение, двоичное и правое сдвиг, без знака вправо и двоичные логические И, 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 вправо
- Определяемое пользователем составное назначение