web-dev-qa-db-ja.com

列挙型定義のチルダ(〜)とは何ですか?

今までずっと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
}

私はそれを見て少し驚いたので、コンパイルしようとしましたが、うまくいきました...しかし、それが何を意味するのか、まだ何をするのかわかりません。何か助け??

143
Hugoware

〜は単項の補数演算子です。オペランドのビットを反転します。

~0 = 0xFFFFFFFF = -1

2の補数演算で、~x == -x-1

〜演算子は、Objective-C/C++/C#/ Java/Javascriptなど、Cから構文を借用したほとんどすべての言語で使用できます。

134
Jimmy

私はそれを思う:

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

もう少し明確になります。

57
Sean Bright
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#には2つの補数があるため、~0 == -1、バイナリ表現ですべてのビットが1である数値。

より良い

All = Cash | Check | CreditCard

解決策。後で別のメソッドを追加する場合は、次のように言ってください。

Paypal = 8 ,

すでにチルダ-Allを使い終わっていますが、もう一方のall-lineを変更する必要があります。そのため、後でエラーが発生しにくくなります。

よろしく

15
blabla999

ちょっとした注意点、あなたが使うとき

All = Cash | Check | CreditCard

Cash | Check | CreditCardAllに評価され、すべての値を含む間はallに等しくない別の値(-1)には評価されません。たとえば、UIで3つのチェックボックスを使用する場合

[] Cash
[] Check
[] CreditCard

そしてそれらの値を合計し、ユーザーがそれらをすべて選択すると、結果の列挙にAllが表示されます。

10
configurator

この質問が明らかになった他の人のために、簡単な~例を紹介します。 この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の答えの類似性に注目してください。

私にとって最も重要なことは、~がenumの通常のコード行と同じ演算子であることです。

9
Mike

これは補数演算子です。ビット単位演算子についてよく参照する記事です。

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

また、msdnはenums記事でそれを使用しており、それがより適切に使用されていることを示しています

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

5
missaghi

〜でいくつかの実験を行ったところ、落とし穴がある可能性があることがわかりました。 LINQPadのこのスニペットについて考えてみましょう。すべての値が一緒にoredされた場合、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 
}
1
Gavin

私が個人的に使用している代替手段は、@ 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
Camilo Martin

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