Что такое тильда (~) в определении перечисления?
Я всегда удивляюсь, что даже после использования С# все это время я все еще могу найти то, о чем я не знал...
Я пробовал искать в Интернете для этого, но использование "~" в поиске не работает для меня так хорошо, и я тоже ничего не нашел на 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
}
Я был немного удивлен, увидев его, поэтому я попытался скомпилировать его, и он сработал... но я до сих пор не знаю, что это значит/делает. Любая помощь?
Ответы
Ответ 1
~ - унарный один оператор дополнения - он переворачивает биты своего операнда.
~0 = 0xFFFFFFFF = -1
в арифметике с двумя дополнениями, ~x == -x-1
оператор ~ может быть найден практически в любом языке, который заимствовал синтаксис из C, включая Objective-C/С++/С#/Java/Javascript.
Ответ 2
Я бы подумал, что:
[Flags]
public enum PurchaseMethod
{
None = 0,
Cash = 1,
Check = 2,
CreditCard = 4,
All = Cash | Check | CreditCard
}
Было бы более понятно.
Ответ 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
}
Из-за двух дополнений в С#, ~0 == -1
, число, где все биты равны 1 в двоичном представлении.
Ответ 4
Лучше, чем
All = Cash | Check | CreditCard
потому что, если вы добавите другой метод позже, скажите:
PayPal = 8 ,
вы уже будете делать с тильдой-все, но должны изменить все-лайн с другим. Таким образом, он менее подвержен ошибкам.
рассматривает
Ответ 5
Просто обратите внимание, когда вы используете
All = Cash | Check | CreditCard
у вас есть дополнительное преимущество, которое Cash | Check | CreditCard
будет оценивать до All
, а не к другому значению (-1), которое не равно всем, включая все значения.
Например, если вы используете три флажка в интерфейсе пользователя
[] Cash
[] Check
[] CreditCard
и суммировать их значения, и пользователь выбирает их все, вы увидите All
в результирующем перечислении.
Ответ 6
Для других, кто понял этот вопрос, у меня есть быстрый ~
пример для обмена. Следующий фрагмент из реализации метода рисования, подробно описанный в этой монографической документации, с большим успехом использует ~
:
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
}
Обратите внимание на это переименование сходства с ответом Шона Яра?
Я думаю, что наиболее важным для меня является то, что ~
- это тот же оператор в перечислении, что и в обычной строке кода.
Ответ 7
Это оператор дополнения,
Вот статья, которую я часто упоминаю для побитовых операторов
http://www.blackwasp.co.uk/CSharpLogicalBitwiseOps.aspx
Также msdn использует его в своей статье перечислений, которая демонстрирует, что она лучше использует
http://msdn.microsoft.com/en-us/library/cc138362.aspx
Ответ 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)
. И ИМХО, +
выглядит лучше.
Ответ 9
Я экспериментировал с ~ и нашел, что у него могут быть подводные камни. Рассмотрим этот фрагмент для LINQPad, который показывает, что значение Enum 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
}
Ответ 10
Просто хочу добавить, если вы используете перечисление [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
}