C # Побитовое ИЛИ требует преобразования с байтом * иногда *

Я обнаружил странную ситуацию в компиляторе C #. Зачем нужен приведенный ниже гипс?

using System;

class Program
{
    private const byte BIT_ZERO_SET = 1;
    private const byte BIT_ONE_SET = 2;
    private const byte BIT_TWO_SET = 4;

    static void Main(string[] args)
    {
        byte b = BIT_ZERO_SET | BIT_ONE_SET;
        Console.WriteLine(b);

        //Does not compile, says needs to cast to int.
        //b = b | BIT_TWO_SET;

        //Compiles...ugly
        b = (byte)(b | BIT_TWO_SET);
        Console.WriteLine(b);

        Console.WriteLine("Press enter.");
        Console.ReadLine();    
    }
}

Спасибо.


person Nate    schedule 14.10.2009    source источник


Ответы (5)


Есть подозрение, что строка:

byte b = BIT_ZERO_SET | BIT_ONE_SET;

фактически обрабатывается компилятором C # в присвоении константе b, а не в побитовой операции - он может это сделать, потому что правая часть выражения полностью определена во время компиляции.

Линия:

//b = b | BIT_TWO_SET;

не компилируется, потому что побитовый оператор ИЛИ продвигает свои элементы и возвращает int, а не байт. Поскольку он включает в себя значение времени выполнения (b), он не может быть скомпилирован в постоянное присваивание, как в предыдущей строке, и требует приведения.

person LBushkin    schedule 14.10.2009
comment
+1, Да, компилятор будет вычислять константные выражения во время компиляции. Это объясняет, почему первое назначение не дает ошибку. - person Henk Holterman; 14.10.2009
comment
@Godeke: Как ты говоришь что-то другое, чем он? Он говорит, что результирующее значение поразрядного OR на byte - это int. - person Adam Robinson; 14.10.2009
comment
Постоянное выражение лица - отвлекающий маневр, вот и все. | никогда не вернет байт без приведения, он возвращает только int, uint, long или ulong. - person Godeke; 14.10.2009
comment
EMCA-334 14.10 Логические операторы - person Godeke; 14.10.2009
comment
К сожалению, тот же прием нельзя использовать для сброса битов, потому что для этого требуется ~BIT_TWO_SET. Это не работает даже для констант, подписанных или беззнаковых (из-за специфики правил неявного приведения) - person Roman Starkov; 26.08.2010

Различные ответы здесь в целом верны, но повсюду разбросана куча разных фактов. Соответствующие моменты:

1) Результат байта | byte - это целое число, потому что | оператор, определенный в байтах.

2) Вычисления, включающие только целые константы времени компиляции, рассматриваются как "проверенная" арифметика; то есть компилятор проверяет, не переполняется ли постоянный результат, и так далее. Приятным следствием этого факта является то, что присвоение целого числа константы времени компиляции переменной или константе меньшего типа автоматически проверяет, подходит ли постоянное целое число меньшему типу. Если это так, то присвоение разрешено без явного приведения. В противном случае возникает ошибка времени компиляции. (Используйте выражение «unchecked», если вы хотите переопределить это поведение.)

3) Присваивания из непостоянных выражений типа int к байту требуют приведения типов, потому что компилятор не может знать, что результат определенно соответствует байту.

4) Составные операторы присваивания автоматически вставляют приведение к типу операнда как часть своей операции, именно поэтому такие выражения, как b | = независимо от того, работают так, как вы ожидаете.

Эти четыре факта должны объяснить все поведение, на которое вы, ребята, указали.

person Eric Lippert    schedule 14.10.2009

Это определенно странно, но то, что происходит, является результатом b|BIT_TWO_SET целого числа.

это работает: b = (byte)(b | BIT_TWO_SET);, потому что в этом случае результатом будет int.

Кроме того, вы можете заменить эту строку на: b |= BIT_TWO_SET;, что работает.

person Erich    schedule 14.10.2009
comment
Я не знал, что есть | =. Спасибо! - person Nate; 14.10.2009
comment
x |= y и x &= ~y - ваши друзья по манипуляциям с битами. - person Christian Hayter; 14.10.2009

        b = (byte)(b | BIT_TWO_SET);

- это все, что вам нужно для его компиляции, по крайней мере, в Visual Studio 2008 против 2.0. Похоже, что | продвигает байт в int, и вам нужно снова понизить его вручную.

Ага ... беглый взгляд на стандарт показывает, что | возвращает int (или uint, или long, или ulong).

person Godeke    schedule 14.10.2009
comment
Да, я изменил свой пост после того, как понял это. Спасибо. - person Nate; 14.10.2009

Вместо того, чтобы использовать

 b = b | BIT_TWO_SET;

использовать это:

 b |= BIT_TWO_SET;

смешно да.

person csharptest.net    schedule 14.10.2009