Что такое тильда (~) в определении enum? - PullRequest
143 голосов
/ 23 декабря 2008

Я всегда удивляюсь, что даже после использования C # все это время мне все еще удается находить вещи, о которых я не знал ...

Я пытался найти в интернете это, но использование «~» в поиске не очень хорошо для меня, и я тоже ничего не нашел в MSDN (не говоря, его там нет)

Я недавно видел этот фрагмент кода, что означает тильда (~)?

/// <summary>
/// Enumerates the ways a customer may purchase goods.
/// </summary>
[Flags]
public enum PurchaseMethod
{   
    All = ~0,
    None =  0,
    Cash =  1,
    Check =  2,
    CreditCard =  4
}

Я был немного удивлен, увидев это, поэтому я попытался скомпилировать его, и это сработало ... но я до сих пор не знаю, что это значит / делает. Любая помощь ??

Ответы [ 10 ]

134 голосов
/ 23 декабря 2008

~ является унарным оператором дополнения - он переворачивает биты своего операнда.

~0 = 0xFFFFFFFF = -1

в двоичной арифметике, ~x == -x-1

Оператор ~ можно найти практически на любом языке, который позаимствовал синтаксис у C, включая Objective-C / C ++ / C # / Java / Javascript.

57 голосов
/ 23 декабря 2008

Я думаю, что:

[Flags]
public enum PurchaseMethod
{
    None = 0,
    Cash = 1,
    Check = 2,
    CreditCard = 4,
    All = Cash | Check | CreditCard
 }

Было бы немного яснее.

22 голосов
/ 23 декабря 2008
public enum PurchaseMethod
{   
    All = ~0, // all bits of All are 1. the ~ operator just inverts bits
    None =  0,
    Cash =  1,
    Check =  2,
    CreditCard =  4
}

Из-за двух дополнений в C #, ~0 == -1, число, где все биты равны 1 в двоичном представлении.

15 голосов
/ 23 декабря 2008

лучше, чем

All = Cash | Check | CreditCard

решение, потому что если вы добавите другой метод позже, скажите:

PayPal = 8 ,

Вы уже закончили с тильдой-Все, но должны изменить всю линию на другую. Так что это менее подвержено ошибкам позже.

1009 * привет *

10 голосов
/ 23 декабря 2008

Просто примечание, когда вы используете

All = Cash | Check | CreditCard

у вас есть дополнительное преимущество, которое Cash | Check | CreditCard будет оценивать как All, а не как другое значение (-1), которое не равно всем, но содержит все значения. Например, если вы используете три флажка в пользовательском интерфейсе

[] Cash
[] Check
[] CreditCard

и суммируйте их значения, и пользователь выберет их все, вы увидите All в результирующем перечислении.

9 голосов
/ 05 ноября 2011

Для тех, кто нашел этот вопрос осветительным, у меня есть быстрый пример ~, которым можно поделиться. В следующем фрагменте реализации метода рисования, который подробно описан в этой документации Mono , используется ~ с большим эффектом:

PaintCells (clipBounds, 
    DataGridViewPaintParts.All & ~DataGridViewPaintParts.SelectionBackground);

Без оператора ~ код, вероятно, будет выглядеть примерно так:

PaintCells (clipBounds, DataGridViewPaintParts.Background 
    | DataGridViewPaintParts.Border
    | DataGridViewPaintParts.ContentBackground
    | DataGridViewPaintParts.ContentForeground
    | DataGridViewPaintParts.ErrorIcon
    | DataGridViewPaintParts.Focus);

... потому что перечисление выглядит так:

public enum DataGridViewPaintParts
{
    None = 0,
    Background = 1,
    Border = 2,
    ContentBackground = 4,
    ContentForeground = 8,
    ErrorIcon = 16,
    Focus = 32,
    SelectionBackground = 64,
    All = 127 // which is equal to Background | Border | ... | Focus
}

Заметили сходство этого перечисления с ответом Шона Брайта?

Я думаю, что наиболее важный вывод для меня заключается в том, что ~ - это тот же оператор в перечислении, что и в обычной строке кода.

5 голосов
/ 23 декабря 2008

Это оператор дополнения, Вот статья, к которой я часто обращаюсь для побитовых операторов

http://www.blackwasp.co.uk/CSharpLogicalBitwiseOps.aspx

Также msdn использует это в своей статье enums, которая демонстрирует, что лучше использовать

http://msdn.microsoft.com/en-us/library/cc138362.aspx

1 голос
/ 15 ноября 2013

Я провел некоторые эксперименты с ~ и обнаружил, что в нем могут быть подводные камни. Рассмотрим этот фрагмент для LINQPad, который показывает, что значение перечисления All не работает должным образом, когда все значения объединены в единое целое.

void Main()
{
    StatusFilterEnum x = StatusFilterEnum.Standard | StatusFilterEnum.Saved;
    bool isAll = (x & StatusFilterEnum.All) == StatusFilterEnum.All;
    //isAll is false but the naive user would expect true
    isAll.Dump();
}
[Flags]
public enum StatusFilterEnum {
      Standard =0,
      Saved =1,   
      All = ~0 
}
1 голос
/ 21 февраля 2013

Альтернатива, которую я лично использую, которая делает то же самое, что и ответ @Sean Bright, но для меня выглядит лучше:

[Flags]
public enum PurchaseMethod
{
    None = 0,
    Cash = 1,
    Check = 2,
    CreditCard = 4,
    PayPal = 8,
    BitCoin = 16,
    All = Cash + Check + CreditCard + PayPal + BitCoin
}

Обратите внимание, что двоичная природа этих чисел, которые являются степенями двух, делает следующее утверждение верным: (a + b + c) == (a | b | c). И ИМХО + выглядит лучше.

0 голосов
/ 04 апреля 2017

Просто хочу добавить, что если вы используете перечисление [Flags], то может быть удобнее использовать побитовый оператор сдвига влево, например:

[Flags]
enum SampleEnum
{
    None   = 0,      // 0
    First  = 1 << 0, // 1b    = 1d
    Second = 1 << 1, // 10b   = 2d
    Third  = 1 << 2, // 100b  = 4d
    Fourth = 1 << 3, // 1000b = 8d
    All    = ~0      // 11111111b
}
...