Qual è la tilde (~) nella definizione di enum?


149

Sono sempre sorpreso che anche dopo aver usato C # per tutto questo tempo, riesco ancora a trovare cose di cui non sapevo ...

Ho provato a cercare su Internet per questo, ma usando "~" in una ricerca non funziona così bene per me e non ho trovato nulla su MSDN (per non dire che non c'è)

Ho visto questo frammento di codice di recente, cosa significa 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
}

Sono stato un po 'sorpreso di vederlo, quindi ho provato a compilarlo, e ha funzionato ... ma non so ancora cosa significhi / faccia. Qualsiasi aiuto??


4
Questa è una soluzione fantastica ed elegante che consente un regolare aggiornamento dell'enum nel tempo. Purtroppo è in conflitto con CA-2217 e genererà un errore se si utilizza l'analisi del codice :( msdn.microsoft.com/en-us/library/ms182335.aspx
Jason Coyne

è il 2020 adesso, e mi ritrovo a pensare le stesse parole di quelle con cui inizi il tuo post. Sono contento di sapere che non sono solo.
funkymushroom,

Risposte:


136

~ è l'operatore di complemento di uno unario - lancia i bit del suo operando.

~0 = 0xFFFFFFFF = -1

nell'aritmetica del complemento a due, ~x == -x-1

l'operatore ~ ​​può essere trovato praticamente in qualsiasi linguaggio che abbia preso in prestito la sintassi da C, incluso Objective-C / C ++ / C # / Java / Javascript.


Questo è figo. Non mi ero reso conto che potresti farlo in un enum. Utilizzeremo sicuramente in futuro
Orion Edwards il

Quindi è l'equivalente di All = Int32.MaxValue? O UInt32.MaxValue?
Joel Mueller,

2
All = (unsigned int) -1 == UInt32.MaxValue. Int32.MaxValue non ha rilevanza.
Jimmy,

4
@ Stevo3000: Int32.MinValue è 0xF0000000, che non è ~ 0 (è in realtà ~ Int32.MaxValue)
Jimmy

2
@Jimmy: Int32.MinValue è 0x80000000. Ha solo un singolo bit impostato (non i quattro che F ti darebbe).
ILMTitan,

59

Penso che:

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

Sarebbe un po 'più chiaro.


10
Certamente è. L'unico effetto positivo dell'unario è che se qualcuno aggiunge all'enumerazione, Tutto lo include automagicamente. Tuttavia, il vantaggio non supera la mancanza di chiarezza.
ctacke,

12
Non vedo come sia più chiaro. Aggiunge ridondanza o ambiguità: "Tutto" significa "esattamente l'insieme di questi 3" o "tutto in questo enum"? Se aggiungo un nuovo valore, dovrei aggiungerlo anche a Tutti? Se vedo che qualcun altro non ha aggiunto un nuovo valore a Tutto, è intenzionale? ~ 0 è esplicito.
Ken,

16
Preferenza personale a parte, se il significato di ~ 0 fosse chiaro per l'OP come lo è per te e per me, non avrebbe mai posto questa domanda in primo luogo. Non sono sicuro di ciò che dice sulla chiarezza di un approccio rispetto all'altro.
Sean Bright,

9
Dato che alcuni programmatori che usano C # potrebbero non sapere ancora cosa significa <<, dovremmo scrivere anche questo codice per maggiore chiarezza? Penso di no.
Kurt Koller,

4
@Paul, è un po 'folle. Smettiamola di usare; in inglese perché molte persone non ne comprendono l'uso. O tutte quelle parole che non conoscono. Wow, un numero così ridotto di operatori, e dovremmo smorzare il nostro codice per le persone che non sanno cosa sono i bit e come manipolarli?
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
}

A causa di due complementi in C #, ~0 == -1il numero in cui tutti i bit sono 1 nella rappresentazione binaria.


Sembra che stiano creando un bit flag per il metodo di pagamento: 000 = None; 001 = Contanti; 010 = Verifica; 100 = carta di credito; 111 = Tutti
Dillie-O

Non è il complemento a due, cioè invertire tutti i bit e aggiungerne uno, in modo che il complemento a due di 0 sia sempre 0. Lo ~ 0 inverte semplicemente tutti i bit o il complemento a uno.
Beardo,

No, il complemento a due sta semplicemente invertendo tutti i bit.
configuratore

2
@configurator - non è corretto. Il complemento è una semplice inversione di bit.
Dave Markle,

c # usa due complementi per rappresentare valori negativi. ovviamente ~ non è due complementi, ma inverte semplicemente tutti i bit. non sono sicuro da dove l'hai preso, Beardo
Johannes Schaub - litb

17

È meglio del

All = Cash | Check | CreditCard

soluzione, perché se si aggiunge un altro metodo in un secondo momento, dire:

PayPal = 8 ,

avrai già finito con la tilde-All, ma dovrai cambiare la linea con l'altra. Quindi è meno soggetto a errori in seguito.

Saluti


È meglio se dici anche perché è meno soggetto a errori. Ad esempio: se memorizzi il valore in un database / file binario e quindi aggiungi un altro flag all'enumerazione, verrebbe incluso in "All", il che significa che "All" significherà sempre tutto, e non solo finché le bandiere sono uguali :).
Aidiakapi,

11

Solo una nota a margine, quando si utilizza

All = Cash | Check | CreditCard

hai il vantaggio aggiunto che Cash | Check | CreditCardvaluterà Alle non ad un altro valore (-1) che non è uguale a tutti mentre contiene tutti i valori. Ad esempio, se si utilizzano tre caselle di controllo nell'interfaccia utente

[] Cash
[] Check
[] CreditCard

e sommare i loro valori, e l'utente li seleziona tutti, Allvedresti nell'enum risultante.


Ecco perché usi myEnum.HasFlag()invece: D
Pyritie il

@Pyritie: in che modo il tuo commento ha qualcosa a che fare con quello che ho detto?
configuratore

come in ... se avessi usato ~ 0 per "All", avresti potuto fare qualcosa di simile All.HasFlag(Cash | Check | CreditCard)e che sarebbe evacuato su true. Sarebbe una soluzione alternativa dal momento ==che non funziona sempre con ~ 0.
Pyritie,

Oh, stavo parlando di quello che vedi nel debugger e con ToString- non dell'uso == All.
configuratore

9

Per gli altri che hanno trovato questa domanda illuminante, ho un rapido ~esempio da condividere. Lo snippet seguente dall'implementazione di un metodo di pittura, come dettagliato in questa documentazione Mono , utilizza ~con grande efficacia:

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

Senza l' ~operatore, il codice sarebbe probabilmente simile a questo:

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

... perché l'enumerazione è simile alla seguente:

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
}

Notate la somiglianza di questo enum con la risposta di Sean Bright?

Penso che il take away più importante per me sia ~lo stesso operatore in un enum come in una normale riga di codice.


Nel tuo secondo blocco di codice, non dovrebbe &essere |s?
ClickRick,

@ClickRick, grazie per la cattura. Il secondo blocco di codice ora ha davvero senso.
Mike,


1

L'alternativa che uso personalmente, che fa la stessa cosa della risposta di @Sean Bright ma mi sembra migliore, è questa:

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

Avviso come la natura binaria di questi numeri, che sono tutte le potenze di due, ha emesso la seguente asserzione vera: (a + b + c) == (a | b | c). E IMHO, +sembra migliore.


1

Ho fatto alcuni esperimenti con ~ e ho scoperto che potrebbe avere delle insidie. Considera questo snippet per LINQPad che mostra che il valore All enum non si comporta come previsto quando tutti i valori vengono ordinati insieme.

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 
}

Lo standard non dovrebbe ottenere il valore 0, ma qualcosa di maggiore di 0.
Adrian Iftode

0

Voglio solo aggiungere, se usi enum [Flags], potrebbe essere più comodo usare l'operatore di spostamento a sinistra bit a bit, in questo modo:

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

Questo non risponde affatto alla domanda.
Servito il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.