枚举定义中的波浪线(~)是什么?

我总是感到惊讶,即使在使用 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
}

看到它时我有点惊讶,所以我尝试编译它,它起作用了... ... 但我仍然不知道它的意思/功能。有人帮忙吗?

29199 次浏览
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的数。

~ 是一元的补运算符——它翻转其操作数的位。

~0 = 0xFFFFFFFF = -1

在二进制补数运算中,~x == -x-1

在几乎所有借用了 C 语法的语言中都可以找到 ~ 运算符,包括 Objective-C/C + +/C #/Java/Javascript。

这是个补运算符, 这是一篇我经常引用的关于位运算符的文章

Http://www.blackwasp.co.uk/csharplogicalbitwiseops.aspx

Msdn 在他们的枚举文章中也使用了它,这说明它使用得更好

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

我会想:

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

那就更清楚了。

它比

All = Cash | Check | CreditCard

解决方案,因为如果稍后添加另一个方法,则说:

PayPal = 8 ,

您将已经完成与波浪所有,但必须改变所有线与其他。所以以后它不太容易出错。

问候

只是附加说明,当你使用

All = Cash | Check | CreditCard

你有额外的好处,Cash | Check | CreditCard将评估到 All,而不是另一个值(- 1) ,这是不等于所有的同时包含所有的值。 例如,如果在 UI 中使用三个复选框

[] Cash
[] Check
[] CreditCard

并对它们的值进行求和,然后用户将它们全部选中,您将在结果枚举中看到 All

对于其他发现这个问题很有启发性的人,我有一个简短的 ~示例来分享。下面是实现油漆方法的代码片段,详见 这个 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
}

注意到这个枚举与肖恩 · 布莱特的答案相似吗?

我认为对我来说最重要的是,~在枚举中的运算符与在正常代码行中的运算符是相同的。

我个人使用的替代方案与@肖恩•布莱特(@Sean Bright)的回答相同,但在我看来更好,就是这个:

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

注意这些数字的二进制性质,它们都是2的幂,如何使下面的断言成立: (a + b + c) == (a | b | c)。恕我直言,+看起来更好。

我已经做了一些实验的 ~ 和发现它可能有陷阱。考虑 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
}

[ 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
}