ИЛИ байтов в C # дает int

У меня есть этот код.

byte dup = 0;
Encoding.ASCII.GetString(new byte[] { (0x80 | dup) });

Когда я пытаюсь скомпилировать, я получаю:

Невозможно неявно преобразовать тип int в byte. Существует явное преобразование (вам не хватает приведения?)

Почему так происходит? Не должно | два байта дают байт? Оба следующих действия работают, гарантируя, что каждый элемент является байтом.

Encoding.ASCII.GetString(new byte[] { (dup) });
Encoding.ASCII.GetString(new byte[] { (0x80) });

person Anthony D    schedule 31.07.2009    source источник
comment
Этот вопрос дает более подробный ответ на него: stackoverflow.com/questions/737781/. У вас есть строковый литерал, который всегда приводится к типу int.   -  person Chris S    schedule 01.08.2009


Ответы (3)


Так заложено в C # и, по сути, восходит к C / C ++ - последний также продвигает операнды до int, вы просто обычно не замечаете, потому что int -> char преобразование является неявным, а в C # его нет. . Это относится не только к |, но и ко всем арифметическим и побитовым операндам - ​​например, добавление двух byte также даст вам int. Я процитирую здесь соответствующую часть спецификации:

Двоичное числовое продвижение происходит для операндов предопределенных бинарных операторов +, -, *, /,%, &, |, ^, ==,! =,>, ‹,> = И‹ =. Двоичное числовое продвижение неявно преобразует оба операнда в общий тип, который в случае нереляционных операторов также становится типом результата операции. Двоичное числовое продвижение заключается в применении следующих правил в том порядке, в котором они указаны здесь:

  • Если один из операндов имеет тип decimal, другой операнд преобразуется в тип decimal, или возникает ошибка времени компиляции, если другой операнд имеет тип float или double.

  • В противном случае, если один из операндов имеет тип double, другой операнд преобразуется в тип double.

  • В противном случае, если один из операндов имеет тип float, другой операнд преобразуется в тип float.

  • В противном случае, если один из операндов имеет тип ulong, другой операнд преобразуется в тип ulong или возникает ошибка времени компиляции, если другой операнд имеет тип sbyte, short, int или long.

  • В противном случае, если один из операндов имеет тип long, другой операнд преобразуется в тип long.

  • В противном случае, если один из операндов имеет тип uint, а другой операнд имеет тип sbyte, short или int, оба операнда преобразуются в тип long.

  • В противном случае, если один из операндов имеет тип uint, другой операнд преобразуется в тип uint.

  • В противном случае оба операнда преобразуются в тип int.

Я не знаю точного объяснения этого, но могу придумать одно. Особенно для арифметических операторов может быть немного удивительно, если (byte)200 + (byte)100 внезапно станет равным 44, даже если это имеет некоторый смысл, если внимательно рассмотреть задействованные типы. С другой стороны, int обычно считается типом, который «достаточно хорош» для арифметических операций с большинством типичных чисел, поэтому, повышая оба аргумента до int, вы получаете своего рода поведение «просто работает» для большинства распространенных случаев.

Что касается того, почему эта логика также применялась к побитовым операторам - я полагаю, это так в основном для согласованности. Это приводит к единому простому правилу, которое является общим для всех небулевых двоичных типов.

Но это все в основном догадки. Эрик Липперт, вероятно, будет тем, кто спросит об истинных мотивах этого решения для C #, по крайней мере (хотя было бы немного скучно, если бы ответ был просто «так это делается в C / C ++ и Java, и это достаточно хорошо. правило как таковое, поэтому мы не видели причин его менять »).

person Pavel Minaev    schedule 31.07.2009
comment
Это действительно интересно. Этот пост заслужил много голосов :) - person Stilgar; 01.08.2009
comment
сладкий ответ! хорошая деталь! Мог бы использовать фрагмент кода, чтобы продемонстрировать, что решит его проблему, но в целом очень интересно. - person Ben Lesh; 01.08.2009
comment
Для полноты картины стоит упомянуть, что операторы |= etc выполняют приведение типов неявно. - person Roman Starkov; 26.08.2010
comment
Это заставляет меня задаться вопросом, связано ли это неявное приведение с затратами на обработку ... Типы данных, с которыми я в конечном итоге выполняю побитовые операции, обычно представляют собой изображения (такие как объединение / разделение битовых плоскостей), поэтому обычно требуется обрабатывать десятки тысяч байтов. - person Nyerguds; 05.03.2020

Литерал 0x80 имеет тип int, поэтому вы не занимаетесь байтами.

То, что вы можете передать его в byte [], работает только потому, что 0x80 (как литерал) находится в диапазоне байтов.

Изменить: даже если 0x80 преобразован в байт, код все равно не будет компилироваться, так как oring bytes все равно даст int. Чтобы он скомпилировался, результат или должен быть приведен: (byte)(0x80|dup)

person Martin v. Löwis    schedule 31.07.2009
comment
правда, но все еще не показывает, как это исправить. простое преобразование 0x80 в byte по-прежнему приводит к сообщению об ошибке, потому что byte | byte = ›int. - person Lucas; 01.08.2009
comment
Извините, проголосовал "против". Не объясняет настоящую проблему здесь: byte | байт = int32. - person Ben Lesh; 01.08.2009
comment
@blesh: нет, проблема не в этом. 0x80 имеет тип int, а не тип byte - вопреки тому, что утверждает OP (и, по-видимому, вы также утверждаете). Итак, это int | byte. Конечно, если бы вы исправили это как (байт) 0x80 | луп, это все равно не сработает по той причине, по которой вы и Лукас говорите. - person Martin v. Löwis; 01.08.2009
comment
Проблема в том, что число в C # (литерал) является целым числом. байт b = 42; является целым числом, а не байтом - person Chris S; 01.08.2009
comment
Автор вопроса преждевременно выбрал это как решение, потому что на самом деле оно не решало проблему. Проблема заключалась в том, что он получал исключение, потому что (0x80 | dup) возвращал int ... ваше решение, гарантирующее, что вы используете оба интеграла or'ed путем преобразования 0x80 в качестве байта, по-прежнему будет вызывать исключение. byte [] {((byte) 0x80 | dup)}; // выдает то же исключение. Этот вопрос нужно модерировать, чтобы кто-то не пришел сюда в поисках ответа и не ушел с неправильным. - person Ben Lesh; 01.08.2009
comment
@blesh: вы ошибаетесь, утверждая, что исключения имеют какое-то отношение к этому. Вместо этого это ошибка времени компиляции, а не ошибка времени выполнения. В любом случае, я добавил в ответ обсуждение байта | байта. - person Martin v. Löwis; 01.08.2009

byte dup = 0;
Encoding.ASCII.GetString(new byte[] { (byte)(0x80 | dup) });

Результат поразрядного ИЛИ (|) для двух байтов всегда является целым числом.

person Ben Lesh    schedule 31.07.2009
comment
Как отмечали люди, 0x80 - это буквальный знак. в любом случае, речь идет о int | byte возвращает int, который не может застрять в byte []. - person Ben Lesh; 01.08.2009