Question Drapeaux enum & opérations binaires vs "chaîne de bits"


Un autre développeur 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 de 0, soit "1000100" pour lundi et vendredi. J'ai préféré (et fortement suggéré) une solution avec un enum Flags et des opérations binaires, je pense que c'est une manière 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, comme j'ai commencé à implémenter un exemple de solution, j'ai réalisé que l'approche des chaînes simples était peut-être plus facile après tout: la chaîne de caractères est certainement plus évidente que "17" si vous ne faites que regarder les données. Et je trouve les opérations bit à 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 soudain avec au moins le même nombre de lignes de code qu'avec la solution string, et je peux difficilement soutenir que le code binaire est plus facile à lire.

Cela étant dit, j'irais encore avec un drap enum et des opérations binaires. Les principaux avantages auxquels je peux penser sont

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

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


35
2017-08-17 02:58


origine


Réponses:


Avantages de l'utilisation de Drapeaux enum:

Les points négatifs de l'utilisation de Drapeaux enum:

  • Représentation de 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 de définir des valeurs "garbage" (par exemple, stringValue = "Sunday")
  • Création de chaîne 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 important de pouvoir regarder la chaîne de bits pour voir ce qui est défini? S'il est difficile de savoir que 17 est lundi et vendredi, vous pouvez toujours utiliser la calculatrice et convertir en binaire. Ou ajoutez une sorte de représentation de chaîne pour une utilisation "display" (ou de débogage). Ce n'est pas cette difficile.


 Il me semble également que si vous voulez que la chaîne de bits soit solide, il vous faudra faire beaucoup d’encapsulation pour l’amener à un niveau d’abstraction que l’enquête Flags fournit déjà. Si l’approche consiste simplement à manipuler directement la chaîne de bits, cela va être difficile à lire (et à comprendre) et probablement source d’erreurs.

par exemple. vous pourriez finir par voir ceci:

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

40
2017-08-17 06:42



Vous ne devez pas créer de structures de données non standard pour remplacer une structure de données standard (dans ce cas, l'énumération intégrée de DayOfWeek). Au lieu de cela, étendre la structure existante. Cela fonctionne essentiellement de la même manière que la méthode des indicateurs de bit 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
2017-08-17 06:38



Faites un cours qui peut contenir la combinaison des jours de la semaine. A l'intérieur de la classe, vous pouvez représenter les données dans un sens ou dans l'autre, mais je choisirais certainement une énumération de drapeaux plutôt qu'une chaîne. En dehors de la classe, vous utilisez simplement les valeurs enum et la logique actuelle 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
2017-08-17 04:10



La question devrait être de savoir si les yeux humains verront 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 encore plus grand, comme un tableau des noms de jours réels).

Cependant, au moins dans toutes les applications que j'ai jamais construites, ce type de données entre dans un petit champ et n'est jamais revu, sauf via le code c # - ce qui signifie que les bitflags sont sans aucun doute les plus simples -. lisible dans du code. Vos collègues veulent vraiment écrire un analyseur de chaînes qui mappe les valeurs 0 et 1 au lieu de construit en et utilisé depuis plus de 40 ans idée des opérations binaires?


1
2017-08-17 03:02



De manière amusante, 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, selon votre modèle, il serait préférable de stocker la liste sous forme de liste contre quiconque la détient).

-- Modifier

Et pour être clair, la performance ne doit pas nécessairement être prise en compte pour ce que vous faites, je pense. Il suffit donc d'aller avec le plus lisible. (Qui, à mon humble avis, sont les drapeaux nommés).


0
2017-08-17 03:00



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


0
2017-08-17 03:12