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


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

10 ответов:

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

~0 = 0xFFFFFFFF = -1

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

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

Я думаю, что:

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

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

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

лучше, чем

All = Cash | Check | CreditCard

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

PayPal = 8 ,

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

в отношении

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

All = Cash | Check | CreditCard

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

[] Cash
[] Check
[] CreditCard

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

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

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
}

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

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

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

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

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

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

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

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

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 
}

просто хочу добавить, если вы используете перечисление [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
}