Apa tilde (~) dalam definisi enum?

149

Saya selalu terkejut bahwa bahkan setelah menggunakan C # selama ini sekarang, saya masih dapat menemukan hal-hal yang tidak saya ketahui tentang ...

Saya sudah mencoba mencari di internet untuk ini, tetapi menggunakan "~" dalam pencarian tidak bekerja dengan baik untuk saya dan saya tidak menemukan apa pun di MSDN (tidak untuk mengatakan itu tidak ada)

Saya melihat potongan kode ini baru-baru ini, apa artinya tilde (~)?

/// <summary>
/// Enumerates the ways a customer may purchase goods.
/// </summary>
[Flags]
public enum PurchaseMethod
{   
    All = ~0,
    None =  0,
    Cash =  1,
    Check =  2,
    CreditCard =  4
}

Saya sedikit terkejut melihatnya jadi saya mencoba mengkompilasinya, dan berhasil ... tapi saya masih tidak tahu apa artinya / tidak. Ada bantuan ??

Hugoware
sumber
4
Ini adalah solusi yang luar biasa dan elegan yang memungkinkan untuk meningkatkan enum dengan lancar dari waktu ke waktu. Sayangnya ini bertentangan dengan CA-2217 dan akan menimbulkan kesalahan jika Anda menggunakan analisis kode :( msdn.microsoft.com/en-us/library/ms182335.aspx
Jason Coyne
ini tahun 2020 sekarang, dan saya mendapati diri saya memikirkan kata-kata yang sama dengan yang Anda gunakan untuk memulai posting Anda. Senang mendengar bahwa saya tidak sendirian.
funkymushroom

Jawaban:

136

~ adalah operator komplemen unary - membalik bit operan.

~0 = 0xFFFFFFFF = -1

dalam aritmatika komplemen dua, ~x == -x-1

operator ~ dapat ditemukan dalam hampir semua bahasa yang meminjam sintaksis dari C, termasuk Objective-C / C ++ / C # / Java / Javascript.

Jimmy
sumber
Itu keren. Saya tidak menyadari Anda bisa melakukan itu dalam enum. Pasti akan digunakan di masa depan
Orion Edwards
Jadi ini setara dengan All = Int32.MaxValue? Atau UInt32.MaxValue?
Joel Mueller
2
All = (unsigned int) -1 == UInt32.MaxValue. Int32.MaxValue tidak memiliki relevansi.
Jimmy
4
@ Stevo3000: Int32.MinValue adalah 0xF0000000, yang bukan ~ 0 (sebenarnya ~ Int32.MaxValue)
Jimmy
2
@ Jimim: Int32.MinValue adalah 0x80000000. Ini hanya memiliki satu set bit (bukan empat yang F akan memberi Anda).
ILMTitan
59

Saya akan berpikir bahwa:

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

Akan sedikit lebih jelas.

Sean Bright
sumber
10
Tentu saja. Satu-satunya efek yang baik dari unary adalah bahwa jika seseorang menambah enumerasi, Semua otomatis memasukkannya. Namun, manfaatnya tidak lebih besar dari kurangnya kejelasan.
ctacke
12
Saya tidak melihat bagaimana itu lebih jelas. Ia menambahkan redundansi atau ambiguitas: apakah "Semua" berarti "set ketiga" ini atau "semua yang ada dalam enum ini"? Jika saya menambahkan nilai baru, haruskah saya juga menambahkannya ke Semua? Jika saya melihat orang lain belum menambahkan nilai baru ke All, apakah itu disengaja? ~ 0 eksplisit.
Ken
16
Selain preferensi pribadi, jika arti ~ 0 sejelas bagi OP seperti halnya bagi Anda dan saya, dia tidak akan pernah mengajukan pertanyaan ini sejak awal. Saya tidak yakin apa yang dikatakan tentang kejelasan dari satu pendekatan versus yang lain.
Sean Bright
9
Karena beberapa programmer yang menggunakan C # mungkin belum tahu apa artinya <<, haruskah kita mengkodekannya juga, untuk kejelasan lebih lanjut? Saya pikir tidak.
Kurt Koller
4
@ Paul, itu agak gila. Mari kita berhenti menggunakan; dalam bahasa Inggris karena banyak orang tidak mengerti penggunaannya. Atau semua kata yang tidak mereka ketahui. Wow, sekumpulan kecil operator, dan kita harus membodohi kode kita untuk orang-orang yang tidak tahu bit apa dan bagaimana memanipulasi mereka?
Kurt Koller
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
}

Karena dua komplemen dalam C #,, ~0 == -1angka di mana semua bit adalah 1 dalam representasi biner.

Johannes Schaub - litb
sumber
Sepertinya mereka membuat tanda bit untuk metode pembayaran: 000 = Tidak ada; 001 = Uang tunai; 010 = Periksa; 100 = Kartu Kredit; 111 = Semua
Dillie-O
Ini bukan komplemen dua, yang membalikkan semua bit dan menambahkan satu, sehingga komplemen dua dari 0 masih 0. The ~ 0 hanya membalikkan semua bit atau komplemen seseorang.
Beardo
Tidak, komplemen dua hanyalah membalik semua bit.
konfigurator
2
@configurator - itu tidak benar. Pelengkap yang merupakan inversi bit sederhana.
Dave Markle
c # menggunakan dua komplemen untuk mewakili nilai negatif. tentu saja ~ bukan dua komplemen, tetapi hanya membalikkan semua bit. Saya tidak yakin dari mana Anda mendapatkannya, Beardo
Johannes Schaub - litb
17

Lebih baik daripada

All = Cash | Check | CreditCard

solusi, karena jika Anda menambahkan metode lain nanti, katakan:

PayPal = 8 ,

Anda sudah selesai dengan tilde-All, tetapi harus mengubah semua-line dengan yang lain. Jadi lebih sedikit kesalahannya nanti.

salam

blabla999
sumber
Lebih baik jika Anda juga mengatakan mengapa lebih sedikit kesalahan. Seperti: jika Anda menyimpan nilai dalam file database / biner dan kemudian menambahkan flag lain ke enumerasi, itu akan dimasukkan dalam 'Semua' yang berarti bahwa 'Semua' akan selalu berarti semua, dan tidak hanya sepanjang bendera adalah sama :).
Aidiakapi
11

Hanya catatan tambahan, saat Anda menggunakannya

All = Cash | Check | CreditCard

Anda memiliki manfaat tambahan yang Cash | Check | CreditCardakan dievaluasi ke Alldan bukan ke nilai lain (-1) yang tidak sama dengan semua saat berisi semua nilai. Misalnya, jika Anda menggunakan tiga kotak centang di UI

[] Cash
[] Check
[] CreditCard

dan menjumlahkan nilainya, dan pengguna memilih semuanya, Anda akan melihat Alldalam hasil enum.

konfigurator
sumber
Itu sebabnya Anda menggunakan myEnum.HasFlag()sebagai gantinya: D
Pyritie
@Pyritie: Bagaimana komentar Anda ada hubungannya dengan apa yang saya katakan?
konfigurator
seperti pada ... jika Anda menggunakan ~ 0 untuk "Semua", Anda dapat melakukan sesuatu seperti All.HasFlag(Cash | Check | CreditCard)dan itu akan terevolusi menjadi true. Akan menjadi solusi karena ==tidak selalu bekerja dengan ~ 0.
Pyritie
Oh, saya berbicara tentang apa yang Anda lihat di debugger dan dengan ToString- bukan tentang menggunakan == All.
konfigurator
9

Untuk orang lain yang menemukan pertanyaan ini menerangi, saya punya ~contoh cepat untuk dibagikan. Cuplikan berikut dari penerapan metode cat, sebagaimana dirinci dalam dokumentasi Mono ini , menggunakan ~efek luar biasa:

PaintCells (clipBounds, 
    DataGridViewPaintParts.All & ~DataGridViewPaintParts.SelectionBackground);

Tanpa ~operator, kodenya mungkin akan terlihat seperti ini:

PaintCells (clipBounds, DataGridViewPaintParts.Background 
    | DataGridViewPaintParts.Border
    | DataGridViewPaintParts.ContentBackground
    | DataGridViewPaintParts.ContentForeground
    | DataGridViewPaintParts.ErrorIcon
    | DataGridViewPaintParts.Focus);

... karena enumerasi terlihat seperti ini:

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
}

Perhatikan kesamaan enum ini dengan jawaban Sean Bright?

Saya pikir yang paling penting untuk saya adalah ~operator yang sama dalam enum seperti pada baris kode normal.

Mike
sumber
Pada blok kode kedua, harus tidak &s menjadi |s?
ClickRick
@ClickRick, terima kasih atas tangkapannya. Blok kode kedua sebenarnya masuk akal sekarang.
Mike
1

Alternatif yang saya gunakan secara pribadi, yang melakukan hal yang sama dari jawaban @Sean Bright tetapi terlihat lebih baik bagi saya, adalah yang ini:

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

Perhatikan bagaimana sifat biner angka-angka, yang semua kekuatan dari dua, membuat pernyataan berikut benar: (a + b + c) == (a | b | c). Dan IMHO, +terlihat lebih baik.

Camilo Martin
sumber
1

Saya telah melakukan beberapa percobaan dengan ~ dan menemukan itu bisa memiliki jebakan. Pertimbangkan potongan ini untuk LINQPad yang menunjukkan bahwa nilai Semua enum tidak berperilaku seperti yang diharapkan ketika semua nilai diurutkan bersama.

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 
}
Gavin
sumber
Standar seharusnya tidak mendapatkan nilai 0, tetapi sesuatu yang lebih besar dari 0.
Adrian Iftode
0

Hanya ingin menambahkan, jika Anda menggunakan [Bendera] enum, maka mungkin lebih nyaman untuk menggunakan operator shift kiri bitwise, seperti ini:

[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
}
sad_robot
sumber
Ini sama sekali tidak menjawab pertanyaan.
Servy