2009-08-17 14 views
34

Un compañero desarrollador sugirió que almacenamos una selección de los días de la semana como cadena de 7 caracteres de 1 y 0, es decir, “1000100” para el lunes y el viernes. Preferí (y sugerí fuertemente) una solución con Flags enum y operaciones bit a bit, creo que es una forma más limpia de hacerlo, y debería ser más fácil de entender para otros desarrolladores.Banderas enumeración y operaciones bit a bit frente a “cadena de bits”

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

Sin embargo, cuando empecé a implementar una solución de la muestra, me di cuenta de que tal vez el enfoque de cadena sencilla era más fácil después de todo: Ciertamente, la cadena de bits es más evidente que “17” si usted está mirando los datos. Y me encuentro con las operaciones de C# bit a bit contrario a la intuición y extremadamente detallado:

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

Por supuesto, esto podría ser envuelto muy bien en los métodos de extensión, pero luego de repente nos terminan con al menos el mismo número de líneas de código al igual que con la solución de cadena, y no puedo argumentar que el código bit a bit es más fácil de leer.

Dicho esto, todavía me gustaría ir con una enumeración banderas y operaciones bit a bit. Los principales beneficios que se me ocurren son

  • Mejor rendimiento
  • Menos espacio necesario para el almacenamiento

Entonces, ¿cómo puedo vender la solución bit a bit a mis colegas? ¿Debería? ¿Cuáles son los otros beneficios de usar este método sobre cadenas? Después de completar el proyecto de muestra, descubrí que el equipo aún optó por la solución basada en cadenas. Necesito algunos argumentos mejores/más fuertes. ¿Por qué debería usar enunciados de banderas en lugar de simples cadenas de bits?

+4

no se ocupa de su pregunta, pero ¿por qué crear su propia enumeración de esto cuando ya hay 'System.DayOfWeek'? –

+8

Los valores enumeraciones System.DayOfWeek son de 0 a 6, no múltiplos de 2 (1, 2, 4, 8, 16, 32 y 64) que necesito para hacer las operaciones bit a bit (AFAIK). Además, LUNES es el primer día de la semana. :) –

+0

Para reducir detallado, se puede escribir: 'if ((días de trabajo y Weekdays.Monday) = 0)' –

Respuesta

40

Beneficios del uso de banderas de enumeración:

negativos de la utilización de banderas de enumeración:?

  • representación de los datos para los seres humanos difíciles de entender (por ejemplo, qué etiquetas están definidas 17)


Ventajas del uso de cadenas de bits:

  • fácil para los programadores para ver qué bits se establecen en la cadena

negativos del uso de cadena de bits:

  • no estándar enfoque
  • más difícil de entender para los programadores no familiares con su diseño
  • Posiblemente más fácil establecer los valores de "basura" (por ejemplo,stringValue = "Sunday")
  • Huelga creación cadena
  • cadena hace falta analizar
  • trabajo de desarrollo adicional
  • Reinventar la rueda (pero ni siquiera una rueda redonda)


¿Qué importancia tiene realmente para poder ver la secuencia de bits para ver qué se establece? Si es difícil saber que 17 es lunes y viernes, siempre puede usar la calculadora y convertir a binario. O agregue algún tipo de representación de cadena para el uso de "visualización" (o depuración). No es que es difícil.


También me parece que si vas a hacer que la cadena de bits se aproxime sólida, necesitarás encapsular un poco para llevarlo al nivel de abstracción que ya proporciona Flags enum. Si el enfoque es simplemente manipular la cadena de bits directamente, entonces será difícil de leer (y comprender) y probablemente sea propenso a errores.

p. Ej. usted puede terminar de ver esto:

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

Puedes obtener lo mejor de ambos mundos usando 0x0001 para el domingo, 0x0010 para el lunes, 0x0100 para el martes, etc. De esta manera, cuando conviertes el valor en hexadecimal, obtienes una lista legible por humanos de días, conservando las ventajas de usar banderas. . –

+0

Tiene dos "Negativos de usar Flags enum", el segundo debería ser "Negativos de utilizar una cadena de bits @ –

+0

@Alexey: Gracias ... corregido ahora. –

0

Curiosamente, ambos métodos son exactamente los mismos; solo el método de banderas es más obvio.

personalmente me quedo con las banderas (aunque potencialmente, dependiendo del modelo, que sería mejor simplemente almacenar la lista como una lista en contra de quien lo limita).

- Editar

Y para que quede claro, el rendimiento en realidad no tiene por qué ser una consideración para lo que está haciendo, creo. Así que solo ve con el más legible. (Que, en mi humilde opinión, son las banderas con nombre).

+0

Acepto, el rendimiento es menos importante (aunque puede ser un problema aquí, teniendo en cuenta el enfoque que están optando). También creo que es más legible, al menos cuando utilizas un conjunto de pequeños métodos de extensión para las operaciones bit a bit (que, aparentemente, es demasiado para ellos asimilar). –

+0

Es fundamental que aprendan operaciones bit a bit de todos modos. Usaría este caso muy simple como una forma de enseñarles, personalmente. –

+0

Sí, de ahí la aplicación de muestra muy simple, que esperaba les enseñara esto. Necesito incluir un par de argumentos súper sólidos con esa muestra ... –

1

La pregunta debería centrarse alrededor de los ojos humanos si alguna vez ver realmente este valor almacenado. Si es así, un formato algo legible por los humanos es obviamente importante (aunque si ese es el caso, yo presentaría un argumento para algo aún más grande, como una serie de nombres de días reales). No obstante, al menos en todas las aplicaciones que he creado, este tipo de información va a un pequeño campo en algún lugar y nunca se vuelve a ver, excepto a través del código C#, lo que significa que los bitflags son definitivamente los más simples. el más humano-legible en el código. Sus colegas realmente quieren escribir un analizador de cadena que los mapas de 0 y 1 de los valores en lugar de utilizar el construida en y utilizado por más de 40 años idea de operaciones bit a bit?

+0

No creo que la sugerencia fuera utilizar una * cadena * para el enfoque 'alternativo' ¿no? Porque eso es solo una locura, estoy de acuerdo. Quizás interpreté mal. –

+0

@silky así es como lo leí: "como una cadena de 7 caracteres de 1 y 0, es decir," 1000100 "para el lunes y el viernes". loco de hecho –

+0

"¿Tus colegas realmente quieren escribir un analizador de cadenas que asigne los valores 0 y 1 a los valores en lugar de utilizar la idea incorporada y utilizada durante más de 40 años de las operaciones bit a bit?" ... respuesta triste y honesta? Sí, esa es la locura que presencio. –

0

El método de Banderas es idiomático (es decir, que es lo que experimentaron los programadores hacen y están acostumbrados a ver y hacer, al menos en C/C++/C# idiomas).

+0

Estoy totalmente de acuerdo: solo necesito ese argumento asesino para darles la vuelta. –

6

Haz una clase que pueda contener la combinación de los días de la semana.Dentro de la clase puede representar los datos de cualquier manera, pero definitivamente iría por una enumeración de banderas en lugar de una cadena. Fuera de la clase solo usas los valores enum, y la lógica real está encapsulada en la clase.

Algo así como:

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

} 

Ejemplo de uso:

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

Creo que malinterpretaste la pregunta ... La pregunta no era "¿Cómo puedo implementar esto usando enumeraciones?" La pregunta era más bien "¿Qué argumentos puedo usar para convencer a mis compañeros de trabajo de que una implementación basada en enum es la más adecuada?" – Alderath

+1

@Alderath: No.Mi respuesta es solo que la implementación debe estar encapsulada en una clase para que realmente no importe cómo se representan los valores. – Guffa

+0

@Guffa Gracias por el ejemplo compacto. – zionpi

23

No debe ser la creación de estructuras de datos no estándar para reemplazar una estructura de datos estándar (en este caso, la enumeración DayOfWeek orden interna) . En cambio, amplíe la estructura existente. Esto funciona esencialmente de la misma manera que el método de indicadores de bit del que hablabas.

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

Ahora usted puede hacer lo siguiente:

Thursday.isWeekday(); // true 
Saturday.isWeekday(); // false 
+0

Simple, inteligente. Me gusta el enfoque de la extensión. Las extensiones para enumeraciones son potentes. – AMissico

Cuestiones relacionadas