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


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/en-us/library/ms182335.aspx
Jason Coyne

2020年になりました。投稿を始めるときと同じ言葉を考えています。私が一人ではないことを聞いてうれしい。
funkymushroom

回答:


136

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

~0 = 0xFFFFFFFF = -1

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

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


カッコいい。列挙型でそれができるとは思いませんでした。間違いなく将来的に使用されます
オリオンエドワーズ

つまり、All = Int32.MaxValueと同等ですか?またはUInt32.MaxValue?
Joel Mueller、

2
すべて=(unsigned int)-1 == UInt32.MaxValue。Int32.MaxValueには関連性がありません。
ジミー

4
@ Stevo3000:Int32.MinValueは0xF0000000であり、これは〜0ではありません(実際には〜Int32.MaxValueです)
ジミー

2
@Jimmy:Int32.MinValueは0x80000000です。単一のビットセットしかありません(Fが与える4つではありません)。
ILMTitan 2013

59

私はそう思う:

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

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


10
確かにそうです。単項の唯一の良い効果は、誰かが列挙に追加した場合、Allが自動的にそれを含めることです。それでも、メリットは明確さの欠如を上回らない。
2008

12
それがもっとはっきりしているのかわかりません。これは冗長性またはあいまいさを追加します。「すべて」は「厳密にこれら3つのセット」または「この列挙のすべて」を意味しますか?新しい値を追加した場合、それもすべてに追加する必要がありますか?他の誰かがAllに新しい値を追加していない場合、それ意図的なものですか?〜0は明示的です。
ケン

16
個人的な好みは別として、〜0の意味があなたにとっても私にとってもOPにとって明確である場合、彼はそもそもこの質問を提示しなかっただろう。あるアプローチと他のアプローチの明確さについて、それが何を言っているのかわかりません。
ショーンブライト

9
C#を使用する一部のプログラマはまだ<<の意味を知らない可能性があるため、より明確にするために、C#をコーディングする必要がありますか?私はそうは思いません。
カートコラー

4
@ポール、それはちょっとクレイジーだ。使用をやめましょう。多くの人がその使用を理解していないので、英語で。または、彼らが知らないすべての言葉。うわー、このような小さな演算子のセットです。ビットが何で、どのように操作するのかわからない人のために、コードを詳しく説明する必要がありますか?
カートコラー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#の2つの補数のため~0 == -1、バイナリ表現ですべてのビットが1である数。


支払い方法のビットフラグを作成しているようです。000=なし。001 =現金; 010 =チェック。100 =クレジットカード。111 =すべて
Dillie-O

これは2の補数ではありません。つまり、すべてのビットを反転して1を追加するので、0の2の補数は0のままです。〜0は単にすべてのビットまたは1の補数を反転します。
Beardo

いいえ、2の補数は単にすべてのビットを反転させています。
コンフィギュレー

2
@configurator-不正解です。1の補数は、ビットの単純な反転です。
Dave Markle、

c#は2つの補数を使用して負の値を表します。もちろん〜は2つの補数ではなく、単にすべてのビットを反転させます。あなたがBeardo、からそれを得たところ私はわかりません
ヨハネス・シャウブ- litb

17

そのより良い

All = Cash | Check | CreditCard

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

PayPal = 8 ,

ティルダ-オールはすでに完了していますが、他のすべての行を変更する必要があります。したがって、後でエラーが発生しにくくなります。

よろしく


エラーが発生しにくい理由も説明した方がよいでしょう。のように:データベース/バイナリファイルに値を格納してから、列挙型に別のフラグを追加すると、その値は「すべて」に含まれます。つまり、「すべて」は常にすべてを意味し、フラグは同じです:)。
アイディアカピ

11

使用時の注意事項

All = Cash | Check | CreditCard

すべての値を含みながら、すべてに等しくない別の値(-1)ではなくCash | Check | CreditCard評価されるという追加の利点Allがあります。たとえば、UIで3つのチェックボックスを使用する場合

[] Cash
[] Check
[] CreditCard

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


それがmyEnum.HasFlag()代わりに使用する理由です:D
Pyritie

@Pyritie:あなたのコメントは、私が言ったこととどのように関係していますか?
コンフィギュレー

のように...「すべて」にAll.HasFlag(Cash | Check | CreditCard)〜0を使用した場合、次のようなことができ、それがtrueに評価されます。==は常に〜0で機能するとは限らないため、回避策になります。
Pyritie

ああ、私はあなたがデバッガで見るものについて話しました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の答えが似ていることに気づきましたか?

私にとって最も重要な~ポイントは、通常のコード行と同じ列挙型の演算子であることです。


2番目のコードブロックでは、&sをs にすべきではありません|か?
ClickRick

@ClickRick、キャッチありがとうございます。2番目のコードブロックは実際には意味があります。
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のこのスニペットを検討してください。これは、すべての値がoredされたときに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 
}

標準では、0値を得るが、何かの0より大きいべきではない
エイドリアンIftode

0

追加したいのは、[Flags] enumを使用する場合、次のようにビット単位の左シフト演算子を使用する方が便利な場合があります。

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

これは質問にまったく答えていません。
サービー2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.