web-dev-qa-db-fra.com

Indique les opérations enum et bit à bit par rapport à la "chaîne de bits"

Un autre développeur nous a suggéré de stocker une sélection de jours de la semaine sous la forme d'une chaîne de 7 caractères de 1 et 0, c'est-à-dire "1000100" pour le lundi et le vendredi. J'ai préféré (et fortement suggéré) une solution avec une énumération Flags et des opérations au niveau du bit, je pense que c'est une façon plus propre de le faire, et cela devrait être plus facile à comprendre pour les autres développeurs.

  [Flags()]
  public enum Weekdays : int
  {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64
  }

Cependant, lorsque j'ai commencé à implémenter un exemple de solution, je me suis rendu compte que peut-être l'approche de chaîne simple était plus facile après tout: la chaîne de bits est certainement plus évidente que "17" si vous regardez simplement les données. Et je trouve les opérations au niveau du bit C # contre-intuitives et extrêmement verbeuses:

Weekdays workDays = Weekdays.Monday | Weekdays.Tuesday;
if ((workDays & Weekdays.Monday) == Weekdays.Monday) 
{...}

Bien sûr, cela pourrait être bien intégré dans les méthodes d'extension, mais nous nous retrouvons soudainement avec au moins le même nombre de lignes de code qu'avec la solution de chaîne, et je peux difficilement affirmer que le code au niveau du bit est plus facile à lire.

Cela étant dit, j'irais toujours avec une énumération des drapeaux et des opérations au niveau du bit. Les principaux avantages auxquels je peux penser sont

  • Meilleure performance
  • Moins d'espace requis pour le stockage

Alors, comment puis-je vendre la solution au niveau du bit à mes collègues? Devrais-je? Quels sont les autres avantages de l'utilisation de cette méthode par rapport aux chaînes? Après avoir terminé l'exemple de projet, j'ai constaté que l'équipe optait toujours pour la solution basée sur les chaînes. J'ai besoin d'arguments meilleurs/plus forts. Pourquoi devriez-vous utiliser des énumérations d'indicateurs plutôt que de simples chaînes de bits?

35
Jakob Gade

Avantages de l'utilisation de l'énumération Flags:

Négatifs de l'utilisation de l'énumération Flags:

  • Représentation des données pour les humains difficiles à comprendre (par exemple, quels indicateurs sont définis pour 17?)


Avantages de l'utilisation d'une chaîne de bits:

  • Facile pour les programmeurs de voir quels bits sont définis dans la chaîne

Négatifs de l'utilisation d'une chaîne de bits:

  • Approche non standard
  • Plus difficile à comprendre pour les programmeurs peu familiers avec votre conception
  • Potentiellement plus facile à définir des valeurs "garbage" (par exemple stringValue = "Sunday")
  • Création de chaînes inutile
  • Analyse de chaîne inutile
  • Travaux de développement supplémentaires
  • Réinventer la roue (mais pas même une roue ronde)


Dans quelle mesure est-il vraiment important de pouvoir regarder la chaîne de bits pour voir ce qui est défini? S'il est difficile de savoir que 17 correspond au lundi et au vendredi, vous pouvez toujours utiliser la calculatrice et la convertir en binaire. Ou ajoutez une sorte de représentation de chaîne pour une utilisation "d'affichage" (ou de débogage). Ce n'est pas ça difficile.


Il me semble également que si vous voulez que la chaîne de bits approche solide, vous devrez faire un peu d'encapsulation pour l'amener à un niveau d'abstraction que l'énumération Flags fournit déjà. Si l'approche consiste à manipuler simplement la chaîne de bits directement, cela sera difficile à lire (et à comprendre) et probablement sujet aux erreurs.

par exemple. vous pourriez finir par voir ceci:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
41

Vous ne devez pas créer des infrastructures de données non standard pour remplacer une structure de données standard (dans ce cas, l'énumération interne DayOfWeek). Au lieu de cela, étendez la structure existante. Cela fonctionne essentiellement de la même manière que la méthode des indicateurs de bits dont vous parliez.

namespace ExtensionMethods
{
    public static class Extensions
    {
        /*
         * Since this is marked const, the actual calculation part will happen at
         * compile time rather than at runtime.  This gives you some code clarity
         * without a performance penalty.
         */
        private const uint weekdayBitMask =
            1 << Monday 
            | 1 << Tuesday
            | 1 << Wednesday
            | 1 << Thursday
            | 1 << Friday;
        public static bool isWeekday(this DayOfWeek dayOfWeek)
        {
            return 1 << dayOfWeek & weekdayBitMask > 0;
        }
    }   
}

Vous pouvez maintenant effectuer les opérations suivantes:

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false
23
Imagist

Faites une classe qui peut contenir la combinaison de jours de la semaine. À l'intérieur de la classe, vous pouvez représenter les données de toute façon, mais je choisirais certainement une énumération des drapeaux plutôt qu'une chaîne. En dehors de la classe, vous utilisez simplement les valeurs d'énumération et la logique réelle est encapsulée dans la classe.

Quelque chose comme:

[Flags]
public enum Days {
   Monday = 1,
   Tuesday = 2,
   Wednesday = 4,
   Thursday = 8,
   Friday = 16,
   Saturday = 32,
   Sunday = 64,
   MondayToFriday = 31,
   All = 127,
   None = 0
}

public class Weekdays {

   private Days _days;

   public Weekdays(params Days[] daysInput) {
      _days = Days.None;
      foreach (Days d in daysInput) {
         _days |= d;
      }
   }

   public bool Contains(Days daysMask) {
      return (_days & daysMask) == daysMask;
   }

   public bool Contains(params Days[] daysMasks) {
      Days mask = Days.None;
      foreach (Days d in daysMasks) {
         mask |= d;
      }
      return (_days & mask) == mask;
   }

}

Exemple d'utilisation:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}
6
Guffa

La question devrait être de savoir si les yeux humains verront jamais réellement cette valeur stockée. Si c'est le cas, un format quelque peu lisible par l'homme est évidemment important (bien que si c'est le cas, je ferais un argument pour quelque chose d'encore plus grand, comme un tableau des noms réels).

Cependant, au moins dans toutes les applications que j'ai jamais construites, ce type de données va dans un petit champ quelque part et n'est jamais revu, sauf via le code c # - ce qui signifie que les bitflags sont certainement les plus simples - ils sont les plus humains- lisible dans le code. Vos collègues veulent vraiment écrire un analyseur de chaîne qui mappe les 0 et les 1 aux valeurs au lieu d'utiliser les idées intégrées et tilisées depuis plus de 40 ans d'opérations au niveau du bit?

1
Rex M

Curieusement, ces deux méthodes sont exactement les mêmes; seule la méthode des drapeaux est plus évidente.

J'irais personnellement avec les drapeaux (bien que potentiellement, selon votre modèle, il serait préférable de simplement stocker la liste en tant que liste contre celui qui la détient).

-- Éditer

Et pour être clair, la performance n'a vraiment pas besoin d'être une considération pour ce que vous faites, je pense. Alors allez-y avec le plus lisible. (Qui, à mon humble avis, est les drapeaux nommés).

0
Noon Silk

La méthode Flags est idiomatique (c'est-à-dire c'est ce que les programmeurs expérimentés font et sont habitués à voir et à faire, au moins dans les langages C/C++/C #).

0
ChrisW