WedX - журнал о программировании и компьютерных науках

Что такое тильда (~) в определении перечисления?

Я всегда удивляюсь, что даже после того, как я все это время использовал 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
}

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


  • Это отличное и элегантное решение, позволяющее плавно обновлять перечисление с течением времени. К сожалению, он конфликтует с CA-2217 и выдаст ошибку, если вы воспользуетесь анализом кода :( msdn.microsoft.com/en-us/library/ms182335.aspx 13.09.2012
  • Сейчас 2020 год, и я ловлю себя на том, что думаю о тех же словах, с которых вы начинаете свой пост. Рад слышать, что я не одинок. 10.07.2020

Ответы:


1

~ — это унарный оператор дополнения до единицы — он переворачивает биты своего операнда.

~0 = 0xFFFFFFFF = -1

в арифметике с дополнением до двух ~x == -x-1

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

22.12.2008
  • Это классно. Я не понимал, что вы можете сделать это в перечислении. Обязательно воспользуюсь в будущем 23.12.2008
  • Значит, это эквивалент All = Int32.MaxValue? Или UInt32.MaxValue? 23.12.2008
  • All = (целое без знака)-1 == UInt32.MaxValue. Int32.MaxValue не имеет значения. 23.12.2008
  • Я бы предположил, что All = Int32.MinValue? 24.04.2009
  • @ Stevo3000: Int32.MinValue равен 0xF0000000, что не равно ~ 0 (на самом деле это ~ Int32.MaxValue) 25.04.2009
  • @Джимми: Int32.MinValue равно 0x80000000. Он имеет только один бит (не четыре, которые F дал бы вам). 27.04.2013
  • @ILMTitan полностью прав. Я слишком быстро написал этот комментарий. 27.04.2013

  • 2

    Я бы подумал, что:

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

    Было бы немного понятнее.

    22.12.2008
  • Это определенно. Единственным хорошим эффектом унарного является то, что если кто-то добавляет к перечислению, All автоматически включает его. Тем не менее, польза не перевешивает отсутствие ясности. 23.12.2008
  • Я не вижу, как это более ясно. Это добавляет либо избыточности, либо двусмысленности: означает ли All именно набор из этих 3 или все в этом перечислении? Если я добавлю новое значение, должен ли я также добавить его ко всем? Если я вижу, что кто-то другой не добавил новое значение в All, это намеренно? ~0 является явным. 02.04.2009
  • Помимо личных предпочтений, если бы значение ~ 0 было так же ясно для ОП, как и для вас и для меня, он бы никогда не задал этот вопрос в первую очередь. Я не уверен, что это говорит о ясности одного подхода по сравнению с другим. 02.04.2009
  • Поскольку некоторые программисты, использующие C#, могут еще не знать, что означает ‹‹, должны ли мы кодировать и его, чтобы было понятнее? Думаю, нет. 19.09.2012
  • @InsidiousForce, забавная история, на днях я прочитал статью, в которой пропагандировалась ТОЧНО эта практика именно по этой причине ... 19.12.2013
  • @ Пол, это какое-то сумасшествие. Давайте перестанем использовать ; на английском языке, потому что многие люди не понимают его использования. Или все те слова, которых они не знают. Ничего себе, такой маленький набор операторов, и мы должны упростить наш код для людей, которые не знают, что такое биты и как ими манипулировать? 04.01.2014
  • ИМО, есть разница между давайте перестанем использовать и давайте использовать более известную альтернативу 23.02.2016
  • Хотя изначально это казалось более ясным, также могут быть ошибки, если кто-то добавит, скажем, Debit = 8 и забудет обновить ВСЕ. Наличие ~ 0 решает эту проблему. Если программист (например, OP) увидит это и не знает, что это такое, он, надеюсь, найдет время, чтобы узнать об этом, но если вы обойдете это, вы никогда не получите его в кодовой базе и не изучите. 20.07.2016

  • 3
    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 в двоичном представлении.

    22.12.2008
  • Похоже, они создают битовый флаг для способа оплаты: 000 = Нет; 001 = Наличные; 010 = проверить; 100 = кредитная карта; 111 = Все 23.12.2008
  • Это не дополнение до двух, это инвертирование всех битов и добавление одного, так что дополнение до двух от 0 по-прежнему равно 0. ~ 0 просто инвертирует все биты или дополнение до единицы. 23.12.2008
  • Нет, дополнение до двух просто инвертирует все биты. 23.12.2008
  • @configurator - это неправильно. Дополнение единиц - это простая инверсия битов. 23.12.2008
  • c# использует два дополнения для представления отрицательных значений. конечно, ~ не является дополнением к двум, а просто инвертирует все биты. я не уверен, откуда ты это взял, Бердо 23.12.2008
  • Просто чтобы уточнить (ради меня). Это не делает ничего особенного в C#, поскольку -1 != 7, поэтому в основном вам придется делать [All = Cash | Проверить | Кредитная карта] ? 17.01.2009

  • 4

    Это лучше, чем

    All = Cash | Check | CreditCard
    

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

    PayPal = 8 ,
    

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

    С уважением

    22.12.2008
  • Лучше, если вы также скажете, почему это менее подвержено ошибкам. Например: если вы сохраните значение в базе данных/двоичном файле, а затем добавите еще один флаг в перечисление, оно будет включено в «Все», что означает, что «Все» всегда будет означать все, а не только до тех пор, пока флаги одинаковые :). 19.04.2012

  • 5

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

    All = Cash | Check | CreditCard
    

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

    [] Cash
    [] Check
    [] CreditCard
    

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

    22.12.2008
  • Вот почему вы используете myEnum.HasFlag() вместо этого :D 07.11.2012
  • @Pyritie: Какое отношение ваш комментарий имеет к тому, что я сказал? 07.11.2012
  • как в... если бы вы использовали ~0 для All, вы могли бы сделать что-то вроде All.HasFlag(Cash | Check | CreditCard) и это будет равно true. Было бы обходным путем, поскольку == не всегда работает с ~ 0. 15.11.2012
  • О, я говорил о том, что вы видите в отладчике и с ToString, а не об использовании == All. 15.11.2012

  • 6

    Для тех, кто нашел этот вопрос проясняющим, у меня есть небольшой ~ пример. Следующий фрагмент реализации метода рисования, подробно описанный в эта документация 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
    }
    

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

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

    04.11.2011
  • Разве во втором блоке кода & не должны быть |? 20.12.2014
  • @ClickRick, спасибо за улов. Второй блок кода теперь действительно имеет смысл. 22.12.2014

  • 7

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

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

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

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

    22.12.2008

    8

    Альтернатива, которую я лично использую, которая делает то же самое, что и ответ @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). И ИМХО, + выглядит лучше.

    21.02.2013

    9

    Я провел несколько экспериментов с ~ и обнаружил, что у него могут быть подводные камни. Рассмотрим этот фрагмент для 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 
    }
    
    15.11.2013
  • Standard не должно получать значение 0, а должно быть больше 0. 20.07.2016

  • 10

    Каждый бит в перечислении [Flags] означает, что что-то включено (1) или отключено (0).
    Оператор ~ используется для инвертирования всех битов числа. Пример: 00001001b превращается в 11110110b.
    Таким образом, ~0 используется для создания значения, в котором разрешены все биты, например 11111111b для 8-битного перечисления.

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

    [Flags]
    enum SampleEnum
    {
        None   = 0,      // 0000b
        First  = 1 << 0, // 0001b
        Second = 1 << 1, // 0010b
        Third  = 1 << 2, // 0100b
        Fourth = 1 << 3, // 1000b
        All    = ~0      // 1111b
    }
    
    04.04.2017
  • Это вообще не отвечает на вопрос. 04.04.2017
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]