Оператор плюс в определении перечисления - PullRequest
6 голосов
/ 16 февраля 2012

Сегодня я наткнулся на использование оператора плюс (+) в определении перечисления. Я был удивлен, увидев, что сопровождающие тесты пройдены. У кого-нибудь есть идеи, где это может быть задокументировано?

public enum ApprovalItemState
{
    Enqueued = 1,
    Approved = 2,
    Denied = 4,
    Acknowledged = 8,
    ApprovalAcknowledged = ApprovalItemState.Approved + ApprovalItemState.Acknowledged,
    DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged
}


[TestClass]
public class ApprovalItemStateTests
{
    [TestMethod]
    public void AreFlagsDeniedAndAcknowledged()
    {
        Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged);
    }

    [TestMethod]
    public void IsDenialAcknowledged()
    {
        Assert.IsTrue(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Denied | ApprovalItemState.Acknowledged));
        Assert.AreEqual(ApprovalItemState.Denied | ApprovalItemState.Acknowledged, (ApprovalItemState)Enum.Parse(typeof(ApprovalItemState), "DenialAcknowledged"));
    }


    [TestMethod]
    public void IsNotDeniedAndApproved()
    {
        Assert.IsFalse(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Approved | ApprovalItemState.Denied));
    }
}

Ответы [ 6 ]

11 голосов
/ 16 февраля 2012

Ответ Рида, конечно, правильный.Я просто подумал, что добавлю интересную мелочь.Во-первых, когда вы внутри перечисления, все члены перечисления находятся в области видимости.Это единственная ситуация в C #, в которой вы можете использовать член перечисления через его неквалифицированное имя!

public enum ApprovalItemState 
{
    Enqueued = 1,
    Approved = 2,
    Denied = 4,
    Acknowledged = 8,
    ApprovalAcknowledged = Approved | Acknowledged,
    DenialAcknowledged =  Denied | Acknowledged 
} 

Второй простой момент заключается в том, что компилятор C # фактически допускает арифметику перечислений с участием других перечислений внутри перечисления!

enum E
{
    E1
}
enum F
{
    F1
}
enum G
{
    G1 = E.E1 + F.F1
}

Обычно это вообще не будет законно;вы не можете добавить два разнородных перечисления вместе и не можете присвоить результат.Компилятор ослабляет эти правила в инициализаторе перечисления, так что вы можете делать такие вещи как:

enum MyFlags
{
    MyReadOnly = FileFlags.ReadOnly,
    ...
10 голосов
/ 16 февраля 2012

Спецификация языка C #, в 14.5, говорится:

Следующие операторы могут быть использованы для значений типов enum: ==,! =, <,>, <=,> = (§7.10.5), двоичный + (§7.8.4), двоичный - (§ 7.8.5), ^, &, | (§7.11.2), ~ (§7.7.4), ++ и - (§7.6.9 и §7.7.5).

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

Однако для определения масок гораздо чаще используется | вместо +. Кроме того, было бы распространенным включить [Flags], если вы собираетесь использовать это как перечисление флагов.

2 голосов
/ 16 февраля 2012

С ссылка C # на enum :

... Каждый тип перечисления имеет базовый тип, который может быть любым целым типом, кроме char. Базовым типом элементов перечисления по умолчанию является int ...

Кстати, более логично (и менее подвержено ошибкам) ​​использовать | вместо + для объединения значений флага перечисления. Например, эта ошибка не вызовет проблемы:

DenialAcknowledged =
    ApprovalItemState.Denied
    | ApprovalItemState.Acknowledged
    | ApprovalItemState.Denied

Но эта ошибка вызовет проблему:

DenialAcknowledged =
    ApprovalItemState.Denied
    + ApprovalItemState.Acknowledged
    + ApprovalItemState.Denied
1 голос
/ 16 февраля 2012

Approved + Acknowledged - это просто константа, поэтому ее можно присвоить как значение элементу enum.Что касается тестов - они работают, потому что значения int являются «счастливыми», поэтому (a + b) == (a | b)

Однако, если вы измените это на что-то подобное:

public enum ApprovalItemState
{
    Enqueued = 1,
    Approved = 2,
    Denied = 7,
    Acknowledged = 18,
    ApprovalAcknowledged = Approved + Acknowledged,
    DenialAcknowledged = Denied + Acknowledged
}

и тесты не пройдут.

0 голосов
/ 16 февраля 2012

Я сломаю один из них для вас.

DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged
DenialAcknowledged = 4 + 8
DenialAcknowledged = 12

Для этого теста:

[TestMethod]
public void AreFlagsDeniedAndAcknowledged()
{
    Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged);
}

Вы проверяете:

ApprovalItemState.DenialAcknowledged == ApprovalItemState.Denied | ApprovalItemState.Acknowledged
12 == 4 | 8
12 == 0100 | 1000 //bitwise operation 
12 == 1100
12 == 12 //convert from binary to decimal

И именно поэтому тест пройден.Не совсем понятно, глядя на код.

0 голосов
/ 16 февраля 2012

Это не удивительно - перечисления представлены целочисленными типами.Вы также можете использовать другие операторы, хотя, если вы собираетесь использовать флаги (что делает этот пример), гораздо лучше использовать атрибут [Flags], чтобы определить их, и лучше распределить биты более четко:

[Flags]
public enum ApprovalItemState
{
    Enqueued = 1 << 0,
    Approved = 1 << 1,
    Denied = 1 << 2,
    Acknowledged = 1 << 3,
    ApprovalAcknowledged = ApprovalItemState.Approved | ApprovalItemState.Acknowledged,
    DenialAcknowledged = ApprovalItemState.Denied | ApprovalItemState.Acknowledged
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...