Je suis toujours surpris que même après avoir utilisé C # pendant tout ce temps maintenant, j'arrive toujours à trouver des choses que je ne connaissais pas ...
J'ai essayé de chercher sur Internet pour cela, mais l'utilisation du "~" dans une recherche ne fonctionne pas si bien pour moi et je n'ai rien trouvé non plus sur MSDN (pour ne pas dire qu'il n'y est pas)
J'ai récemment vu cet extrait de code, que signifie le 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
}
J'étais un peu surpris de le voir alors j'ai essayé de le compiler, et cela a fonctionné ... mais je ne sais toujours pas ce que cela signifie/fait. De l'aide??
~ est l'opérateur complémentaire unaire - il retourne les bits de son opérande.
~0 = 0xFFFFFFFF = -1
dans l'arithmétique du complément à deux, ~x == -x-1
l'opérateur ~ peut être trouvé dans pratiquement tous les langages empruntant la syntaxe à C, y compris Objective-C/C++/C #/Java/Javascript.
Je pense que:
[Flags]
public enum PurchaseMethod
{
None = 0,
Cash = 1,
Check = 2,
CreditCard = 4,
All = Cash | Check | CreditCard
}
Serait un peu plus clair.
public enum PurchaseMethod
{
All = ~0, // all bits of All are 1. the ~ operator just inverts bits
None = 0,
Cash = 1,
Check = 2,
CreditCard = 4
}
En raison de deux compléments en C #, ~0 == -1
, le nombre où tous les bits sont 1 dans la représentation binaire.
C'est mieux que le
All = Cash | Check | CreditCard
solution, car si vous ajoutez une autre méthode plus tard, dites:
Paypal = 8 ,
vous aurez déjà terminé avec le tilde-All, mais vous devez changer la ligne complète avec l'autre. C'est donc moins sujet aux erreurs plus tard.
cordialement
Juste une note latérale, lorsque vous utilisez
All = Cash | Check | CreditCard
vous avez l'avantage supplémentaire que Cash | Check | CreditCard
correspondrait à All
et non à une autre valeur (-1) qui n'est pas égale à tout en contenant toutes les valeurs. Par exemple, si vous utilisez trois cases à cocher dans l'interface utilisateur
[] Cash
[] Check
[] CreditCard
et additionner leurs valeurs, et l'utilisateur les sélectionne tous, vous verriez All
dans l'énumération résultante.
Pour ceux qui ont trouvé cette question éclairante, j'ai un rapide ~
exemple à partager. L'extrait suivant de l'implémentation d'une méthode Paint, comme détaillé dans cette documentation Mono , utilise ~
avec grand effet:
PaintCells (clipBounds,
DataGridViewPaintParts.All & ~DataGridViewPaintParts.SelectionBackground);
Sans le ~
opérateur, le code ressemblerait probablement à ceci:
PaintCells (clipBounds, DataGridViewPaintParts.Background
| DataGridViewPaintParts.Border
| DataGridViewPaintParts.ContentBackground
| DataGridViewPaintParts.ContentForeground
| DataGridViewPaintParts.ErrorIcon
| DataGridViewPaintParts.Focus);
... car l'énumération ressemble à ceci:
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
}
Remarquez la similitude de cette énumération avec la réponse de Sean Bright?
Je pense que le plus important à retenir pour moi est que ~
est le même opérateur dans une énumération que dans une ligne de code normale.
C'est un opérateur complémentaire, voici un article auquel je me réfère souvent pour les opérateurs au niveau du bit
http://www.blackwasp.co.uk/CSharpLogicalBitwiseOps.aspx
Aussi msdn l'utilise dans leur article enums qui démontre qu'il utilise mieux
J'ai fait quelques expériences avec le ~ et je trouve qu'il pourrait avoir des pièges. Considérez cet extrait de code pour LINQPad qui montre que la valeur Enum All ne se comporte pas comme prévu lorsque toutes les valeurs sont orées ensemble.
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
}
L'alternative que j'utilise personnellement, qui fait la même chose que la réponse de @Sean Bright mais qui me semble meilleure, est celle-ci:
[Flags]
public enum PurchaseMethod
{
None = 0,
Cash = 1,
Check = 2,
CreditCard = 4,
Paypal = 8,
BitCoin = 16,
All = Cash + Check + CreditCard + Paypal + BitCoin
}
Remarquez comment la nature binaire de ces nombres, qui sont tous des puissances de deux, rend l'affirmation suivante vraie: (a + b + c) == (a | b | c)
. Et à mon humble avis, +
regarde mieux.
Je veux juste ajouter, si vous utilisez l'énumération [Flags], alors il peut être plus pratique d'utiliser l'opérateur de décalage gauche au niveau du bit, comme ceci:
[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
}