枚举定义中的波浪号(〜)是什么?


149

我一直很惊讶,即使在这段时间里一直使用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
}

看到它令我有些惊讶,因此我尝试对其进行了编译,并且它起作用了……但是我仍然不知道它的含义/作用。有帮助吗?


4
这是一个很棒且优雅的解决方案,可以随着时间的推移平稳地升级枚举。不幸的是,它与CA-2217冲突,并且如果您使用代码分析,将引发错误:( msdn.microsoft.com/zh-cn/library/ms182335.aspx
Jason Coyne

现在是2020年,我发现自己的想法与发布帖子时的想法相同。很高兴听到我并不孤单。
funkymushroom

Answers:


136

〜是一元运算符的补码运算符-它翻转其操作数的位。

~0 = 0xFFFFFFFF = -1

用二进制补码算术 ~x == -x-1

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


这很酷。我没有意识到您可以在枚举中做到这一点。将来肯定会使用
Orion Edwards

所以这等于All = Int32.MaxValue吗?还是UInt32.MaxValue?
乔尔·穆勒

2
全部=(无符号整数)-1 == UInt32.MaxValue。Int32.MaxValue没有关联。
吉米

4
@ Stevo3000:Int32.MinValue为0xF0000000,不是〜0(实际上是〜Int32.MaxValue)
Jimmy

2
@Jimmy:Int32.MinValue是0x80000000。它只设置了一个位(不是F给您的四个位)。
ILMTitan

59

我认为:

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

会更加清楚。


10
必然是。一元代码的唯一好处是,如果有人将枚举添加到了枚举,则All会自动将其包括在内。但是,好处仍然不胜于缺乏明确性。
ctacke

12
我不知道这有多清楚。它增加了冗余性或歧义性:“全部”是指“恰好是这3个的集合”还是“此枚举中的所有”?如果添加新值,是否还应该将其添加到全部?如果我看到其他人没有为All添加新值,那是故意的吗?〜0是显式的。

16
除了个人喜好,如果〜0的含义对OP和您我一样清晰,那么他绝不会首先提出这个问题。我不确定这说明一种方法相对于另一种方法的清晰度。
肖恩·布莱特

9
由于一些使用C#的程序员可能还不知道<<意味着什么,因此为了更清楚起见,我们也应该围绕它进行编码吗?我觉得不是。
Kurt Koller 2012年

4
@Paul,这有点疯狂。让我们停止使用; 之所以用英文,是因为很多人不了解它的用法。或所有他们不知道的单词。哇,这么少的运算符,我们应该为不知道什么是位以及如何操纵它们的人简化代码。
Kurt Koller 2014年

22
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的数字。


看起来他们正在为付款方式创建一个位标志:000 =无;001 =现金;010 =检查;100 =信用卡;111 =全部
Dillie-O

它不是二进制补码,而是将所有位取反并加1,以便二进制补码0仍为0。〜0只是将所有位或一个补码取反。
Beardo

不,二进制补码只是将所有位取反。
配置器

2
@configurator-这是不正确的。补码是位的简单反转。
戴夫·马克尔

c#使用两个补码表示负值。当然〜不是两个补码,而是简单地将所有位取反。我不确定你从哪里得到的,比尔多
约翰内斯·绍布-利特伯

17

它比

All = Cash | Check | CreditCard

解决方案,因为如果以后添加另一种方法,请说:

PayPal = 8 ,

您将已经使用tilde-All完成了所有操作,但必须更改其他所有行。因此,以后不太容易出错。

问候


如果您还说为什么它不那么容易出错,那会更好。像:如果将值存储在数据库/二进制文件中,然后向枚举添加另一个标志,则它将包含在“全部”中,这意味着“全部”将始终表示全部,而不仅仅是标志是相同的:)。
Aidiakapi '04

11

只是一点说明,当您使用

All = Cash | Check | CreditCard

您将获得附加收益,该收益Cash | Check | CreditCard将包含All另一个值(-1),而该值不等于所有值,同时又包含所有值。例如,如果您在用户界面中使用三个复选框

[] Cash
[] Check
[] CreditCard

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


这就是为什么您myEnum.HasFlag()改用:D
Pyritie 2012年

@Pyritie:您的评论与我所说的有什么关系?
配置程序

就像在...中,如果您All.HasFlag(Cash | Check | CreditCard)将〜0 用作“全部”,则可以执行类似的操作,而这些操作将真实化。这将是一种解决方法,因为==并不总是与〜0一起使用。
Pyritie 2012年

哦,我说的是您在调试器中看到的内容,而ToString不是使用== All
配置程序

9

对于发现此问题有启发意义的其他人,我有一个简单的~例子可以分享。以下是Mono方法中详细描述的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
}

注意到此枚举与Sean Bright的答案相似吗?

我认为对我来说最重要的一点~是,枚举中的运算符与普通代码行中的运算符相同。


在第二个代码块中,&s 不是|吗?
ClickRick 2014年

@ClickRick,谢谢。实际上,第二个代码块现在有意义。
迈克(Mike)


1

我个人使用的替代方法是这样的:它与@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)。恕我直言,+看起来更好。


1

我对〜做过一些实验,发现它可能存在陷阱。考虑一下LINQPad的代码段,该代码段显示当所有值一起使用时,所有枚举值的行为均不符合预期。

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 
}

标准不应该得到0值,但一些大于0
阿德里安Iftode

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
}

这根本没有回答问题。
Servy '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.